[Android] PorterDuff使用实例----实现新浪微博图片下载效果
先上效果图,如demo_sinaweibo.gif
由效果图,下半部分是简单的效果叠加,上半部分是新浪微博加载图片显示进度的效果,显示进度的半透明区域只与根据背景图的非透明区域叠加,背景图的透明区域仍为透明。
为实现此要求,联想到APIDemos中的com.example.android.apis.graphics.Xfermodes,可以自定义组件在组件的绘制过程中设置PorterDuff.Mode即可实现。
另效果图中显示当下载进度超过50%时,重新设置了背景图。
本次自定义组件选择继承ImageView来实现,名为PorterDuffView。将ImageView新增一porterduffMode。在该模式下,将可显示图片加载进度;否则按ImageView原有规则显示图片。
1.PorterDuffView的XML编码
设置PorterDuffView的porterduffMode,可有两种方式,一为在xml中设置,一为在代码设置。
xml中设置的实现:
在/res/values下新建一"attrs.xml",内容如下:
- <?xml version="1.0" encoding="UTF-8"?>
- <resources>
- <!--请参阅
- [android_sdk]\platforms\android-n\data\res\values\attrs.xml
- -->
- <declare-styleable name="porterduff.PorterDuffView">
- <attr name="porterduffMode" format="boolean"></attr>
- </declare-styleable>
- </resources>
在此声明中,属性名为"porterduffMode",对其赋值范围为boolean型。赋值范围的规范可参考:[android_sdk]\platforms\android-n\data\res\values\attrs.xml
有声明后,在layout下的布局文件,需首先在原有基础上添加一命名空间,代码如下:
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:porterduff="http://schemas.android.com/apk/res/lab.sodino.porterduff"
- android:orientation="vertical"
- .... ....
- ></LinearLayout>
命名空间名为"porterduff",赋值规则为"http://schemas.android.com/apk/res/"+App包名。
在布局文件中使用PorterDuffView时,几乎与ImageView一致,设置porterduffMode如下:
- <lab.sodino.porterduff.PorterDuffView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/porterDuffView"
- android:src="@drawable/loading"
- android:layout_gravity="center"
- porterduff:porterduffMode="true"
- ></lab.sodino.porterduff.PorterDuffView>
另,在代码中设置porterduffMode为直接调用setPorterDuffMode(boolean),参数为true即可。
2.PorterDuffView的Java编码
需要重写ImageView的onDraw()方法。
当其porterduffMode值为true时,显示图片加载进度。否则按ImageView 的规则显示图片。
由效果图可知,需要生成一半透明的前景图。
生成前景图的代码为:
- /** 生成一宽与背景图片等同高为1像素的Bitmap,。 */
- private static Bitmap createForegroundBitmap(int w) {
- Bitmap bm = Bitmap.createBitmap(w, FG_HEIGHT, Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(bm);
- Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
- p.setColor(FOREGROUND_COLOR);
- c.drawRect(0, 0, w, FG_HEIGHT, p);
- return bm;
- }
为节约内存消耗,生成的前景图Bitmap其高度只有1像素。那么在onDraw()方法中,需要根据加载进度,循环滴绘制叠加区域。代码如下:
- int tH = height - (int) (progress * height);
- for (int i = 0; i < tH; i++) {
- canvas.drawBitmap(bitmapFg, tmpW, tmpH + i, paint);
- }
在onDraw()方法中,调用Paint.setXfermode()绘制完叠加区域后,应再次对其设置值为null取消PorterDuff效果。
加载区域的进度值由PorterDuffView.setProgress()决定,因为每次设定新的进度后,应该调用 invalidate()及时刷新界面。
效果图中进度过50%时更改了背景图,方法为PorterDuffView.setBitmap(),该方法将重新计算Bitmap的宽高,并生成新的前景图,调用ImageView.setImageBitmap()请求对组件重新布局及刷新界面。
本文为Sodino所有,转载请注明出处:http://blog.csdn.net/sodino/article/details/7741236
以下贴出Java代码,XML请看官自行实现:
- ActPorterDuff.java
- package lab.sodino.porterduff;
- import android.app.Activity;
- import android.graphics.BitmapFactory;
- import android.os.Bundle;
- import android.widget.SeekBar;
- import android.widget.SeekBar.OnSeekBarChangeListener;
- public class ActPorterDuff extends Activity implements OnSeekBarChangeListener {
- private SeekBar seekbar;
- private PorterDuffView porterDuffView;
- private int currentId;
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- seekbar = (SeekBar) findViewById(R.id.seekbar);
- seekbar.setOnSeekBarChangeListener(this);
- float progress = seekbar.getProgress() * 1.0f / seekbar.getMax();
- porterDuffView = (PorterDuffView) findViewById(R.id.porterDuffView);
- porterDuffView.setProgress(progress);
- }
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- porterDuffView.setProgress(progress * 1.0f / seekbar.getMax());
- if (progress > 50 && currentId != R.drawable.loading_2) {
- currentId = R.drawable.loading_2;
- porterDuffView.setBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.loading_2));
- } else if (progress <= 50 && currentId != R.drawable.loading) {
- currentId = R.drawable.loading;
- porterDuffView.setBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.loading));
- }
- }
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
- public void onStopTrackingTouch(SeekBar seekBar) {
- }
- }
- PorterDuffView.java
- package lab.sodino.porterduff;
- import java.text.DecimalFormat;
- import android.content.Context;
- import android.content.res.TypedArray;
- import android.graphics.Bitmap;
- import android.graphics.Canvas;
- import android.graphics.Paint;
- import android.graphics.PorterDuff;
- import android.graphics.PorterDuffXfermode;
- import android.graphics.drawable.BitmapDrawable;
- import android.graphics.drawable.Drawable;
- import android.util.AttributeSet;
- import android.util.Log;
- import android.widget.ImageView;
- /**
- * 自定义组件实现新浪微博的图片加载效果。<br/>
- *
- * @author Sodino E-mail:sodinoopen@hotmail.com
- * @version Time:2012-7-9 上午01:55:04
- */
- public class PorterDuffView extends ImageView {
- /** 前景Bitmap高度为1像素。采用循环多次填充进度区域。 */
- public static final int FG_HEIGHT = 1;
- /** 下载进度前景色 */
- // public static final int FOREGROUND_COLOR = 0x77123456;
- public static final int FOREGROUND_COLOR = 0x77ff0000;
- /** 下载进度条的颜色。 */
- public static final int TEXT_COLOR = 0xff7fff00;
- /** 进度百分比字体大小。 */
- public static final int FONT_SIZE = 30;
- private Bitmap bitmapBg, bitmapFg;
- private Paint paint;
- /** 标识当前进度。 */
- private float progress;
- /** 标识进度图片的宽度与高度。 */
- private int width, height;
- /** 格式化输出百分比。 */
- private DecimalFormat decFormat;
- /** 进度百分比文本的锚定Y中心坐标值。 */
- private float txtBaseY;
- /** 标识是否使用PorterDuff模式重组界面。 */
- private boolean porterduffMode;
- /** 标识是否正在下载图片。 */
- private boolean loading;
- public PorterDuffView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(context, attrs);
- }
- /** 生成一宽与背景图片等同高为1像素的Bitmap,。 */
- private static Bitmap createForegroundBitmap(int w) {
- Bitmap bm = Bitmap.createBitmap(w, FG_HEIGHT, Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(bm);
- Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
- p.setColor(FOREGROUND_COLOR);
- c.drawRect(0, 0, w, FG_HEIGHT, p);
- return bm;
- }
- private void init(Context context, AttributeSet attrs) {
- if (attrs != null) {
- // //////////////////////////////////////////
- // int count = attrs.getAttributeCount();
- // for (int i = 0; i < count; i++) {
- // LogOut.out(this, "attrNameRes:" +
- // Integer.toHexString(attrs.getAttributeNameResource(i))//
- // + " attrName:" + attrs.getAttributeName(i)//
- // + " attrResValue:" + attrs.getAttributeResourceValue(i, -1)//
- // + " attrValue:" + attrs.getAttributeValue(i)//
- // );
- // }
- // //////////////////////////////////////////
- TypedArray typedArr = context.obtainStyledAttributes(attrs, R.styleable.porterduff_PorterDuffView);
- porterduffMode = typedArr.getBoolean(R.styleable.porterduff_PorterDuffView_porterduffMode, false);
- }
- Drawable drawable = getDrawable();
- if (porterduffMode && drawable != null && drawable instanceof BitmapDrawable) {
- bitmapBg = ((BitmapDrawable) drawable).getBitmap();
- width = bitmapBg.getWidth();
- height = bitmapBg.getHeight();
- // LogOut.out(this, "width=" + width + " height=" + height);
- bitmapFg = createForegroundBitmap(width);
- } else {
- // 不符合要求,自动设置为false。
- porterduffMode = false;
- }
- paint = new Paint();
- paint.setFilterBitmap(false);
- paint.setAntiAlias(true);
- paint.setTextSize(FONT_SIZE);
- // 关于FontMetrics的详情介绍,可见:
- // http://xxxxxfsadf.iteye.com/blog/480454
- Paint.FontMetrics fontMetrics = paint.getFontMetrics();
- // 注意观察本输出:
- // ascent:单个字符基线以上的推荐间距,为负数
- Log.d("ANDROID_LAB", "ascent:" + fontMetrics.ascent//
- // descent:单个字符基线以下的推荐间距,为正数
- + " descent:" + fontMetrics.descent //
- // 单个字符基线以上的最大间距,为负数
- + " top:" + fontMetrics.top //
- // 单个字符基线以下的最大间距,为正数
- + " bottom:" + fontMetrics.bottom//
- // 文本行与行之间的推荐间距
- + " leading:" + fontMetrics.leading);
- // 在此处直接计算出来,避免了在onDraw()处的重复计算
- txtBaseY = (height - fontMetrics.bottom - fontMetrics.top) / 2;
- decFormat = new DecimalFormat("0.0%");
- }
- public void onDraw(Canvas canvas) {
- if (porterduffMode) {
- int tmpW = (getWidth() - width) / 2, tmpH = (getHeight() - height) / 2;
- // 画出背景图
- canvas.drawBitmap(bitmapBg, tmpW, tmpH, paint);
- // 设置PorterDuff模式
- paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DARKEN));
- // canvas.drawBitmap(bitmapFg, tmpW, tmpH - progress * height,
- // paint);
- int tH = height - (int) (progress * height);
- for (int i = 0; i < tH; i++) {
- canvas.drawBitmap(bitmapFg, tmpW, tmpH + i, paint);
- }
- // 立即取消xfermode
- paint.setXfermode(null);
- int oriColor = paint.getColor();
- paint.setColor(TEXT_COLOR);
- paint.setTextSize(FONT_SIZE);
- String tmp = decFormat.format(progress);
- float tmpWidth = paint.measureText(tmp);
- canvas.drawText(decFormat.format(progress), tmpW + (width - tmpWidth) / 2, tmpH + txtBaseY, paint);
- // 恢复为初始值时的颜色
- paint.setColor(oriColor);
- } else {
- Log.d("ANDROID_LAB", "onDraw super");
- super.onDraw(canvas);
- }
- }
- public void setProgress(float progress) {
- if (porterduffMode) {
- if (this.progress != progress) {
- this.progress = progress;
- // 刷新自身。
- invalidate();
- }
- }
- }
- public void setBitmap(Bitmap bg) {
- if (porterduffMode) {
- bitmapBg = bg;
- width = bitmapBg.getWidth();
- height = bitmapBg.getHeight();
- bitmapFg = createForegroundBitmap(width);
- Paint.FontMetrics fontMetrics = paint.getFontMetrics();
- txtBaseY = (height - fontMetrics.bottom - fontMetrics.top) / 2;
- setImageBitmap(bg);
- // 请求重新布局,将会再次调用onMeasure()
- // requestLayout();
- }
- }
- public boolean isLoading() {
- return loading;
- }
- public void setLoading(boolean loading) {
- this.loading = loading;
- }
- public void setPorterDuffMode(boolean bool) {
- porterduffMode = bool;
- }
- }
[Android] PorterDuff使用实例----实现新浪微博图片下载效果的更多相关文章
- Android:通过滤镜实现点击图片变暗效果
实现点击图片(ImageView)变暗效果,有一个较简单的方法,就是讲目标图片设置为背景图片(setBackground),再创建一个selector.xml文件,里面放置一张普通状态时的透明图片,一 ...
- Android 自定义 ViewPager 打造千变万化的图片切换效果
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38026503 记得第一次见到ViewPager这个控件,瞬间爱不释手,做东西的主 ...
- Android 轮播图Banner切换图片的效果
Android XBanner使用详解 2018年03月14日 08:19:59 AND_Devil 阅读数:910 版权声明:本文为博主原创文章,未经博主允许不得转载. https://www. ...
- Android 自定义 HorizontalScrollView 打造再多图片(控件)也不怕 OOM 的横向滑动效果
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38140505 自从Gallery被谷歌废弃以后,Google推荐使用ViewPa ...
- picasso-强大的Android图片下载缓存库
编辑推荐:稀土掘金,这是一个针对技术开发者的一个应用,你可以在掘金上获取最新最优质的技术干货,不仅仅是Android知识.前端.后端以至于产品和设计都有涉猎,想成为全栈工程师的朋友不要错过! pica ...
- picasso_强大的Android图片下载缓存库
tag: android pic skill date: 2016/07/09 title: picasso-强大的Android图片下载缓存库 [本文转载自:泡在网上的日子 参考:http://bl ...
- Picasso – Android系统的图片下载和缓存类库
Picasso – Android系统的图片下载和缓存类库 Picasso 是Square开源的一个用于Android系统下载和缓存图片的项目.该项目和其他一些下载图片项目的主要区别之一是:使用4.0 ...
- 毕加索的艺术——Picasso,一个强大的Android图片下载缓存库,OkHttpUtils的使用,二次封装PicassoUtils实现微信精选
毕加索的艺术--Picasso,一个强大的Android图片下载缓存库,OkHttpUtils的使用,二次封装PicassoUtils实现微信精选 官网: http://square.github.i ...
- Android性能优化-减小图片下载大小
原文链接 https://developer.android.com/topic/performance/network-xfer.html 内容概要 理解图片的格式 PNG JPG WebP 如何选 ...
随机推荐
- EF code first 生成edmx文件
通过下面的代码,你就可以拿到EF心中的地图 —— edmx文件. using (var context = new Context()) { XmlWriterSettings settings = ...
- json数据获取
今天Pei讲了一个android获取json数据的方式吧 代码什么的没看懂,反正知道就是那么一回事,用AsyncTask线程来获取json数据,我也不知道这样说对不对 估计以后回过来看的时候会发现一大 ...
- c 单链表反转(不添加新结点空间)
最近复习考研,加上一直都将"算法"放在很高的位置,所以,蛮重视算法的.不多说了,其实这个问题,不难理解的. 主要代码: //反转单链表. void reverse(linklist ...
- linux(vi)多行注释和取消注释.
//comment1,'ctrl+v' to VISUAL BLOCK mode.2,'j' or 'k' to select/deselect lines.3,'I' to INSERT mode. ...
- VC++6.0 下配置 pthread库2010年12月12日 星期日 13:14VC下的pthread多线程编程 转载
VC++6.0 下配置 pthread库2010年12月12日 星期日 13:14VC下的pthread多线程编程 转载 #include <stdio.h>#include &l ...
- Ehcache入门(一)——开发环境的搭建
EhCache 是一个纯Java的进程内缓存框架,具有快速.精干等特点,是Hibernate中默认的CacheProvider. 那么.如何搭建Ehcache的开发环境呢? 1.下载相关的jar包,这 ...
- ecshop模板如何修改详细图解
ecshop模板如何修改?很多人在问这个问题,今天就以图解的方式给大家详细说下.相信学完之后,你会很清楚如何修改ecshop模板,不管你是初学者还是程序高手. 1, ecshop的模板结构 ecsho ...
- Linux入门 (笔记)
Man can conquer nature. "人定胜天" 一.基本操作 1.重要快捷键 Tab 补全命令.目录.参数.文件名等 Ctrl+c 强制终止当前的程序 Ctrl+ ...
- WDCP控制面板安装卸载
安装 安装源码 WDCP提供两种安装模式,一种是源码安装,一种是RPM包安装,众所周知,源码安装虽然安装时要比RPM包安装繁琐,但是使用上要节省内存.所以我这里主要介绍源码安装. 首先是下载安装源代码 ...
- PHP如何防止SQL注入及开发安全 53
PHP如何防止SQL注入及开发安全 [php] function inject_check($sql_str) { $check=eregi('select|insert|update|de ...