改善用户体验,用图片的自身变化以及进度通知摆脱传统的进度条,okhttp,Canvas,Paint实现

转载请注明出处:王亟亟的大牛之路

从最开始的白页面等待,到后来的进度条告知用户,到现在的WebBO/微信这种先下缩略图点击才重新下大图的方式,我们开发者对用户感知的注意度越来越高,昨天刷微博的时候看到他们是用一个灰色转圈圈的实现,所以就萌生的今天要做的内容的启发(我是在不知道给这种实现取什么名字,就写了一大堆,感觉在哪见过类似的但是,忘了出自于哪了)

先上下效果:

GIF软件继续把我的效果给吃了。。大家可以自己跑一下,看效果。


HOW to do?

1.我们的图片来自于网络,如果是本地,也不需要这一系列的操作了,几乎是瞬间。那么我们的下载过程中的进度通知得一直刷新。
2.我们得让图片一直在更新UI至少产生差异性,不然用户不知道这是干什么

带着这2个问题,我们来说一下,首先是下载,用Volley,和api自带的一些库都可以实现,往里面加回调传进度出来就行了,这里楼主用的是git大牛的https://github.com/hongyangAndroid/okhttp-utils
对okhttp的封装简单易用。

第二个问题就是怎么实现,我们有了进度,那就是画。这边用的是源生的bitmap canvas paint三剑客,具体怎么做,等会代码会贴。



包结构:

实现类:MainActivity
父类:Father(提供抽象方法)
图片数据源:Config(正常图片太小了,强行 试了好多次)

public class MainActivity extends Father {
    private Button btn;
    private ImageView imageView;
    private Paint paint;
    private Canvas canvas;
    private Bitmap bitmap;
    private String fileUrl;
    private float imageWidth, imageHeight;
    private  Paint paint2;

    @Override
    public int getLayout() {
        return R.layout.activity_main;
    }

    @Override
    public void init() {
        btn=(Button)findViewById(R.id.btn);
        imageView = (ImageView) findViewById(R.id.imageView);
        imageWidth = (int) getResources().getDimension(R.dimen.image_width);
        imageHeight = (int) getResources().getDimension(R.dimen.image_height);
    }

    @Override
    public void setClick() {
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                fileUrl = Environment.getExternalStorageDirectory().getAbsolutePath() + "/wjj/" + "meizi.jpg";

                if (checkFile(fileUrl)) {
                    LogUtils.d("--->onResume checkFile==true");
                    imageView.setImageBitmap(BitmapFactory.decodeFile(fileUrl));
                    Toast.makeText(MainActivity.this,"图片已经存在于SD卡内",Toast.LENGTH_SHORT).show();
                } else {
                    LogUtils.d("--->onResume checkFile==false");
                    DownLoadImage(ConFig.ImageUrl);
                }
            }
        });
    }

    @Override
    public void Logic() {
        LogUtils.d("--->MainActivity Logic getWidth  " + imageWidth + " getHeight " + imageHeight);
        if (bitmap == null) {
            bitmap = Bitmap.createBitmap((int) imageWidth,
                    (int) imageHeight, Bitmap.Config.ARGB_8888);
            canvas = new Canvas(bitmap);
            canvas.drawColor(getResources().getColor(R.color.Gray));
        }
        paint = new Paint();
        paint.setColor(getResources().getColor(R.color.White));
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));

        paint2 = new Paint();

        paint2.setColor(getResources().getColor(R.color.Gray));
        paint2.setTextSize(90);
        paint2.setTextAlign(Paint.Align.CENTER);
        imageView.setImageBitmap(bitmap);
    }

    @Override
    protected void onResume() {
        super.onResume();
    }

    private void DownLoadImage(String ImageUrl) {
        OkHttpUtils
                .get()
                .url(ImageUrl)
                .build()
                .execute(new FileCallBack(Environment.getExternalStorageDirectory().getAbsolutePath() + "/wjj/", "meizi.jpg") {

                    @Override
                    public void inProgress(float progress) {
                        LogUtils.d((int) (100 * progress) + "高度等于 " + imageHeight * progress);
                        canvas.drawRect(0, 0, imageWidth, imageHeight * progress, paint);
                        canvas.drawText((int) (100 * progress) + "%", imageWidth / 2, imageHeight / 2 - 200, paint2);

                        imageView.setImageBitmap(bitmap);
                    }

                    @Override
                    public void onError(Request request, Exception e) {
                        LogUtils.e("onError :" + e.getMessage());
                    }

                    @Override
                    public void onResponse(File file) {
                        LogUtils.d("onResponse :" + file.getAbsolutePath());
                        imageView.setImageBitmap(BitmapFactory.decodeFile(file.getAbsolutePath()));
                    }
                });
    }

    /**
     * 控件的高度
     *
     * @param view 控件View
     * @return 返回控件的高度
     */
    public static int getHeight(View view) {
        if (view == null) {
            throw new IllegalArgumentException("view is null");
        }

        view.measure(0, 0);
        return view.getMeasuredHeight();
    }

    /**
     * 获取控件的宽度
     *
     * @param view 控件
     * @return 返回控件的宽度
     */
    public static int getWidth(View view) {
        if (view == null) {
            throw new IllegalArgumentException("view is null");
        }

        view.measure(0, 0);
        return view.getMeasuredWidth();
    }

    private boolean checkFile(String checkFile) {
        File mFile = new File(fileUrl);
        //若该文件存在
        if (mFile.exists()) {
            return true;
        } else {
            return false;
        }
    }
}

代码都在上面了,来说一下里面的坑。

坑1
image.getHeight()和image.getWidth,粗略看来是不是如果我们设置的不是wrap_content那么一定能获取到参数?
事实是NO,无论怎么整他都是0,那怎么获取呢?方法如下!

//------------------------------------------------方法一
int w = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
int h = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
imageView.measure(w, h);
int height =imageView.getMeasuredHeight();
int width =imageView.getMeasuredWidth();
LogUtils.d("\n"+height+","+width);  

//-----------------------------------------------方法二
ViewTreeObserver vto = imageView.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
    public boolean onPreDraw() {
        int height = imageView.getMeasuredHeight();
        int width = imageView.getMeasuredWidth();
        LogUtils.d("\n"+height+","+width);
        return true;
    }
});
//-----------------------------------------------方法三
ViewTreeObserver vto2 = imageView.getViewTreeObserver();
vto2.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        imageView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
        LogUtils.d("\n\n"+imageView.getHeight()+","+imageView.getWidth());
    }
});    

这些都能获取到参数,但是只要离开那行代码,东西就没了还是0 .WHY 什么鬼!?

onCreate方法执行完了,我们定义的控件才会被度量(measure),所以我们在onCreate方法里面通过view.getHeight()获取控件的高度或者宽度肯定是0,因为它自己还没有被度量,也就是说他自己都不知道自己有多高,而你这时候去获取它的尺寸,是不行的.

那有两个办法,1.我去设一张图片,然后图片的大小就是我控件的大小。2.也就是我用的方法,人工控件的大小。脑洞来源于:http://stackoverflow.com/questions/19271609/imageview-getwidth-returns-0

文章中是这样

  imageWidth = (int) getResources().getDimension(R.dimen.image_width);
  imageHeight = (int) getResources().getDimension(R.dimen.image_height);

那么有人要问了,我实际场景下也设定死吗?

我们的业务层不可能整一个页面都是一张图,那么UI给与我们界面定稿的时候总有一个相对大小,而我们的控件大小可以在适配的大小区间里来游走(反正在现实场景时,这图片总是点击放大才去加载,而那时候不已经全屏看了?你还在意控件大小么?)

所以,这个实现的使用场景就是在看大图时(一定得大,不然怎么都是瞬间),才有他的使用价值。



小解释:

代码中的paint 是用来画背景的 paint2 是用来画进度的文字的。

文字位置是 x=宽/2 y=长/2

图片更新方式:总长/100=每1%的Y向长度,然后每次更新的进度数*这个1%Y长度就行。

文章中的图片最后还是存于SD卡里的,如果想直接下载完Set到ImageView上的话就别存把流转一下就好了。

源码地址:https://github.com/ddwhan0123/BlogSample/tree/master/ImageLoadingAnim

记得点个赞哦!!

时间: 2025-01-30 15:59:06

改善用户体验,用图片的自身变化以及进度通知摆脱传统的进度条,okhttp,Canvas,Paint实现的相关文章

3个可以改善用户体验的AngularJS指令介绍

  这篇文章主要介绍了3个可以改善用户体验的AngularJS指令,AngularJS是一款具有很高人气的JavaScript框架,需要的朋友可以参考下 1.头像图片 为了在你的应用中展示头像图片,你需要使用用户的电子邮件地址,将地址转换为小写并使用md5加密该字符串.所以聪明的做法是使用指令来做到这些,并且可以复用. ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29   /* * A

后台产品交互设计:了解用户目标改善用户体验

文章描述:更贴近业务及用户--交互设计工作方式转变感言. 引子: 年初时交互设计师与产品经理座谈,议题关于交互设计师应从哪个工作环节切入更能发挥其自身价值.结论是交互随产品经理参与到项目初期需求挖掘阶段,更早的参与了解业务需求及用户需求,与产品经理共同产出PRD(带有用户体验的高保真原型部分由交互设计师着力产出,原型好处:方便用户参与可用性测试.整个产品团队更直观地把握设计要求,优势是显而易见的).取而代之目前项目正式启动,产品经理产出详细的PRD后,交互设计师再参与到项目里参与原型设计,有点迟

改善用户体验的 3 个 AngularJS 指令 【已翻译100%】

AngularJS指令可以为给你的访问者提供更好的用户体验,比如通过展示用户头像来使页面看起来更具个性化.在你的注册表单中,可以在电子邮箱地址一栏的旁边展示一个头像,指示用户输入的是否是一个正确的邮件地址.如果在你的表单中有可选输入项,你可以默认隐藏它们,当用户点击时再展示出来,并且立刻自动将焦点对准第一个输入框.这些方法非常容易实现,并且可以通过指令来获得复用. 你有许多方式来构建AngularJS指令.关于如果创建用户指令已经有非常多的教程和指导(所以我不打算在此描述一些基本的东西): An

为了改善用户体验,知乎应该重新审视

"知乎是不是已经变成了论坛?","知乎和百度知道现在有什么区别?","知乎与李毅吧之间的本质区别是什么?",在现在定位为"高端社交问答社区"的zhihu.com出现这样的新手提问,总会让那些资深用户有一种受到了冒犯的感觉,而他们的回答往往也会带上一丝不屑和鄙视:"这样的问题要是再多一些,那两者就真的没区别了." 实际上,两者之间至少有一个明显的区别,那就是在知乎指南中说明的:"知乎有些地方其实很像

oelove v3.X 重点改善用户体验及优化技术底层

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 oelove v3.X 重点改善用户体验及优化技术底层等方面. 许多用户一直在问,oelove新版本v3到底出增加了什么功能,首先感谢这些一直关注我们成长的站长朋友们,你们的关注是我们发展的动力.在oelove v2.X使用中,许多站长通过线下与线上的方式结合取得了良好的效果,部份有经验的站长并己有开始盈利.更多的地方站长的运营方式较单一更需

改善用户体验的五款jQuery插件分享_jquery

1.菜单栏 清爽美观的菜单栏既能够向用户提供充分的吸引人的内容,又能让网站得体大方. Cool Animated Menu 演示|下载 2. tab 一个非常cool的tab既能够节省空间又能第一时间吸引用户眼球,这是提高用户体验的重要手段. Feature List 演示|下载 3. 登录/注册 登录/注册机制是巩固老用户和吸引新用户的重要方式,人性化的登录/注册机制才能为网站带来越来越多的用户.登录/注册机制要注意细节,最好把登录和注册放在一起,操作要便捷高效. A Cool Login S

SNS网站运营秘籍:注重前期引导改善用户体验

中介交易 SEO诊断 淘宝客 云主机 技术大厅 随着SNS的普及,UCenter Home(SNS)像Discuz!(BBS)一样成为网络社区建站的标配.为了更好的让更多的网络社区用好UCenter Home,我们特别准备UCenter Home(SNS)建站"运营秘籍"系列专题,把很多UCenter Home(SNS)实战应用的小技巧,与广大站长朋友们一起分享,愿UCenter Home和广大站长朋友一起成长. 注重前期引导,改善用户体验 一.邀请好友登陆页设置: 前段时间有朋友向我

天气通发布全新版本 改善用户体验

作为业内起步最早的天气应用,天气通始终改善用户体验,并把功能创新作为一种特色坚持至今.近日天气通同时发布了AndroidV2.4版和iPhoneV2.0版,又一次开创了天气应用的全新表现方式.天气通的老用户可能对去年8月新增的"天气随手拍"功能记忆犹新,通过用户拍摄照片即可让其他用户直观的看到天气情况,淡化了天气资讯一贯的数据罗列,是一次很大胆的尝试.新版本中"天气随手拍"功能已华丽变身为"实景天气",优化后的入口位于首页,用户可以一键进入,看照

提升网站用户体验—WebP 图片的高效使用

一.WebP 的由来 现代图像压缩技术对我们的生活方式影响很大.数码相机能将上千张高质量图片存储到一张内存卡里.智能手机可以与邻近设备快速分享高分辨率的图片.网站与手机等移动设备能快速展示各种富媒体. 然而,如果图片只能以最原始的格式进行存储的话,以上所有都只是纸上空谈. 在 APP.浏览器或 PC 端.还是移动端等各种设备里,通常使用 JPEG 这种损耗较大的格式对图片进行高效率的管理,而使用 PNG 这种失真较小的格式传送图表.图标以及图画等. 然而,在过去几年间,尽管视频的格式发展迅速,图