[Android] PorterDuff使用实例----实现新浪微博图片下载效果

来源:互联网 发布:微信朋友圈发淘宝链接 编辑:程序博客网 时间:2024/05/23 15:51

From: http://blog.csdn.net/sodino/article/details/7741236

先上效果图,如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",内容如下:

[html] view plaincopy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <resources>  
  3.     <!--请参阅  
  4.     [android_sdk]\platforms\android-n\data\res\values\attrs.xml  
  5.     -->  
  6.     <declare-styleable name="porterduff.PorterDuffView">  
  7.         <attr name="porterduffMode" format="boolean"></attr>  
  8.     </declare-styleable>  
  9. </resources>  

在此声明中,属性名为"porterduffMode",对其赋值范围为boolean型。赋值范围的规范可参考:[android_sdk]\platforms\android-n\data\res\values\attrs.xml

有声明后,在layout下的布局文件,需首先在原有基础上添加一命名空间,代码如下:
[html] view plaincopy
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2. xmlns:porterduff="http://schemas.android.com/apk/res/lab.sodino.porterduff"  
  3.    android:orientation="vertical"  
  4. .... ....  
  5. ></LinearLayout>  

命名空间名为"porterduff",赋值规则为"http://schemas.android.com/apk/res/"+App包名。
在布局文件中使用PorterDuffView时,几乎与ImageView一致,设置porterduffMode如下:
[html] view plaincopy
  1. <lab.sodino.porterduff.PorterDuffView  
  2.     android:layout_width="wrap_content"  
  3.     android:layout_height="wrap_content"  
  4.     android:id="@+id/porterDuffView"  
  5.     android:src="@drawable/loading"  
  6.     android:layout_gravity="center"  
  7.     porterduff:porterduffMode="true"  
  8. ></lab.sodino.porterduff.PorterDuffView>  

另,在代码中设置porterduffMode为直接调用setPorterDuffMode(boolean),参数为true即可。


2.PorterDuffView的Java编码
需要重写ImageView的onDraw()方法。
当其porterduffMode值为true时,显示图片加载进度。否则按ImageView 的规则显示图片。
由效果图可知,需要生成一半透明的前景图。
生成前景图的代码为:
[java] view plaincopy
  1. /** 生成一宽与背景图片等同高为1像素的Bitmap,。 */  
  2. private static Bitmap createForegroundBitmap(int w) {  
  3.     Bitmap bm = Bitmap.createBitmap(w, FG_HEIGHT, Bitmap.Config.ARGB_8888);  
  4.     Canvas c = new Canvas(bm);  
  5.     Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);  
  6.     p.setColor(FOREGROUND_COLOR);  
  7.     c.drawRect(00, w, FG_HEIGHT, p);  
  8.     return bm;  
  9. }  

为节约内存消耗,生成的前景图Bitmap其高度只有1像素。那么在onDraw()方法中,需要根据加载进度,循环滴绘制叠加区域。代码如下:
[java] view plaincopy
  1. int tH = height - (int) (progress * height);  
  2. for (int i = 0; i < tH; i++) {  
  3.     canvas.drawBitmap(bitmapFg, tmpW, tmpH + i, paint);  
  4. }  

在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请看官自行实现:
[java] view plaincopy
  1. ActPorterDuff.java  
  2.   
  3. package lab.sodino.porterduff;  
  4.   
  5. import android.app.Activity;  
  6. import android.graphics.BitmapFactory;  
  7. import android.os.Bundle;  
  8. import android.widget.SeekBar;  
  9. import android.widget.SeekBar.OnSeekBarChangeListener;  
  10.   
  11. public class ActPorterDuff extends Activity implements OnSeekBarChangeListener {  
  12.     private SeekBar seekbar;  
  13.     private PorterDuffView porterDuffView;  
  14.     private int currentId;  
  15.   
  16.     public void onCreate(Bundle savedInstanceState) {  
  17.         super.onCreate(savedInstanceState);  
  18.         setContentView(R.layout.main);  
  19.   
  20.         seekbar = (SeekBar) findViewById(R.id.seekbar);  
  21.         seekbar.setOnSeekBarChangeListener(this);  
  22.         float progress = seekbar.getProgress() * 1.0f / seekbar.getMax();  
  23.         porterDuffView = (PorterDuffView) findViewById(R.id.porterDuffView);  
  24.         porterDuffView.setProgress(progress);  
  25.     }  
  26.   
  27.     public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {  
  28.         porterDuffView.setProgress(progress * 1.0f / seekbar.getMax());  
  29.   
  30.         if (progress > 50 && currentId != R.drawable.loading_2) {  
  31.             currentId = R.drawable.loading_2;  
  32.             porterDuffView.setBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.loading_2));  
  33.         } else if (progress <= 50 && currentId != R.drawable.loading) {  
  34.             currentId = R.drawable.loading;  
  35.             porterDuffView.setBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.loading));  
  36.         }  
  37.     }  
  38.   
  39.     public void onStartTrackingTouch(SeekBar seekBar) {  
  40.     }  
  41.   
  42.     public void onStopTrackingTouch(SeekBar seekBar) {  
  43.     }  
  44. }  

[java] view plaincopy
  1. PorterDuffView.java  
  2.   
  3. package lab.sodino.porterduff;  
  4.   
  5. import java.text.DecimalFormat;  
  6.   
  7. import android.content.Context;  
  8. import android.content.res.TypedArray;  
  9. import android.graphics.Bitmap;  
  10. import android.graphics.Canvas;  
  11. import android.graphics.Paint;  
  12. import android.graphics.PorterDuff;  
  13. import android.graphics.PorterDuffXfermode;  
  14. import android.graphics.drawable.BitmapDrawable;  
  15. import android.graphics.drawable.Drawable;  
  16. import android.util.AttributeSet;  
  17. import android.util.Log;  
  18. import android.widget.ImageView;  
  19.   
  20. /** 
  21.  * 自定义组件实现新浪微博的图片加载效果。<br/> 
  22.  *  
  23.  * @author Sodino E-mail:sodinoopen@hotmail.com 
  24.  * @version Time:2012-7-9 上午01:55:04 
  25.  */  
  26. public class PorterDuffView extends ImageView {  
  27.     /** 前景Bitmap高度为1像素。采用循环多次填充进度区域。 */  
  28.     public static final int FG_HEIGHT = 1;  
  29.     /** 下载进度前景色 */  
  30.     // public static final int FOREGROUND_COLOR = 0x77123456;  
  31.     public static final int FOREGROUND_COLOR = 0x77ff0000;  
  32.     /** 下载进度条的颜色。 */  
  33.     public static final int TEXT_COLOR = 0xff7fff00;  
  34.     /** 进度百分比字体大小。 */  
  35.     public static final int FONT_SIZE = 30;  
  36.     private Bitmap bitmapBg, bitmapFg;  
  37.     private Paint paint;  
  38.     /** 标识当前进度。 */  
  39.     private float progress;  
  40.     /** 标识进度图片的宽度与高度。 */  
  41.     private int width, height;  
  42.     /** 格式化输出百分比。 */  
  43.     private DecimalFormat decFormat;  
  44.     /** 进度百分比文本的锚定Y中心坐标值。 */  
  45.     private float txtBaseY;  
  46.     /** 标识是否使用PorterDuff模式重组界面。 */  
  47.     private boolean porterduffMode;  
  48.     /** 标识是否正在下载图片。 */  
  49.     private boolean loading;  
  50.   
  51.     public PorterDuffView(Context context, AttributeSet attrs) {  
  52.         super(context, attrs);  
  53.         init(context, attrs);  
  54.     }  
  55.   
  56.     /** 生成一宽与背景图片等同高为1像素的Bitmap,。 */  
  57.     private static Bitmap createForegroundBitmap(int w) {  
  58.         Bitmap bm = Bitmap.createBitmap(w, FG_HEIGHT, Bitmap.Config.ARGB_8888);  
  59.         Canvas c = new Canvas(bm);  
  60.         Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);  
  61.         p.setColor(FOREGROUND_COLOR);  
  62.         c.drawRect(00, w, FG_HEIGHT, p);  
  63.         return bm;  
  64.     }  
  65.   
  66.     private void init(Context context, AttributeSet attrs) {  
  67.         if (attrs != null) {  
  68.             // //////////////////////////////////////////  
  69.             // int count = attrs.getAttributeCount();  
  70.             // for (int i = 0; i < count; i++) {  
  71.             // LogOut.out(this, "attrNameRes:" +  
  72.             // Integer.toHexString(attrs.getAttributeNameResource(i))//  
  73.             // + " attrName:" + attrs.getAttributeName(i)//  
  74.             // + " attrResValue:" + attrs.getAttributeResourceValue(i, -1)//  
  75.             // + " attrValue:" + attrs.getAttributeValue(i)//  
  76.             // );  
  77.             // }  
  78.             // //////////////////////////////////////////  
  79.   
  80.             TypedArray typedArr = context.obtainStyledAttributes(attrs, R.styleable.porterduff_PorterDuffView);  
  81.             porterduffMode = typedArr.getBoolean(R.styleable.porterduff_PorterDuffView_porterduffMode, false);  
  82.         }  
  83.         Drawable drawable = getDrawable();  
  84.         if (porterduffMode && drawable != null && drawable instanceof BitmapDrawable) {  
  85.             bitmapBg = ((BitmapDrawable) drawable).getBitmap();  
  86.             width = bitmapBg.getWidth();  
  87.             height = bitmapBg.getHeight();  
  88.             // LogOut.out(this, "width=" + width + " height=" + height);  
  89.             bitmapFg = createForegroundBitmap(width);  
  90.         } else {  
  91.             // 不符合要求,自动设置为false。  
  92.             porterduffMode = false;  
  93.         }  
  94.   
  95.         paint = new Paint();  
  96.         paint.setFilterBitmap(false);  
  97.         paint.setAntiAlias(true);  
  98.         paint.setTextSize(FONT_SIZE);  
  99.   
  100.         // 关于FontMetrics的详情介绍,可见:  
  101.         // http://xxxxxfsadf.iteye.com/blog/480454  
  102.         Paint.FontMetrics fontMetrics = paint.getFontMetrics();  
  103.         // 注意观察本输出:  
  104.         // ascent:单个字符基线以上的推荐间距,为负数  
  105.         Log.d("ANDROID_LAB""ascent:" + fontMetrics.ascent//  
  106.                 // descent:单个字符基线以下的推荐间距,为正数  
  107.                 + " descent:" + fontMetrics.descent //  
  108.                 // 单个字符基线以上的最大间距,为负数  
  109.                 + " top:" + fontMetrics.top //  
  110.                 // 单个字符基线以下的最大间距,为正数  
  111.                 + " bottom:" + fontMetrics.bottom//  
  112.                 // 文本行与行之间的推荐间距  
  113.                 + " leading:" + fontMetrics.leading);  
  114.         // 在此处直接计算出来,避免了在onDraw()处的重复计算  
  115.         txtBaseY = (height - fontMetrics.bottom - fontMetrics.top) / 2;  
  116.   
  117.         decFormat = new DecimalFormat("0.0%");  
  118.     }  
  119.   
  120.     public void onDraw(Canvas canvas) {  
  121.         if (porterduffMode) {  
  122.             int tmpW = (getWidth() - width) / 2, tmpH = (getHeight() - height) / 2;  
  123.             // 画出背景图  
  124.             canvas.drawBitmap(bitmapBg, tmpW, tmpH, paint);  
  125.             // 设置PorterDuff模式  
  126.             paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DARKEN));  
  127.             // canvas.drawBitmap(bitmapFg, tmpW, tmpH - progress * height,  
  128.             // paint);  
  129.             int tH = height - (int) (progress * height);  
  130.             for (int i = 0; i < tH; i++) {  
  131.                 canvas.drawBitmap(bitmapFg, tmpW, tmpH + i, paint);  
  132.             }  
  133.   
  134.             // 立即取消xfermode  
  135.             paint.setXfermode(null);  
  136.             int oriColor = paint.getColor();  
  137.             paint.setColor(TEXT_COLOR);  
  138.             paint.setTextSize(FONT_SIZE);  
  139.             String tmp = decFormat.format(progress);  
  140.             float tmpWidth = paint.measureText(tmp);  
  141.             canvas.drawText(decFormat.format(progress), tmpW + (width - tmpWidth) / 2, tmpH + txtBaseY, paint);  
  142.             // 恢复为初始值时的颜色  
  143.             paint.setColor(oriColor);  
  144.         } else {  
  145.             Log.d("ANDROID_LAB""onDraw super");  
  146.             super.onDraw(canvas);  
  147.         }  
  148.     }  
  149.   
  150.     public void setProgress(float progress) {  
  151.         if (porterduffMode) {  
  152.             if (this.progress != progress) {  
  153.                 this.progress = progress;  
  154.                 // 刷新自身。  
  155.                 invalidate();  
  156.             }  
  157.         }  
  158.     }  
  159.   
  160.     public void setBitmap(Bitmap bg) {  
  161.         if (porterduffMode) {  
  162.             bitmapBg = bg;  
  163.             width = bitmapBg.getWidth();  
  164.             height = bitmapBg.getHeight();  
  165.   
  166.             bitmapFg = createForegroundBitmap(width);  
  167.   
  168.             Paint.FontMetrics fontMetrics = paint.getFontMetrics();  
  169.             txtBaseY = (height - fontMetrics.bottom - fontMetrics.top) / 2;  
  170.   
  171.             setImageBitmap(bg);  
  172.             // 请求重新布局,将会再次调用onMeasure()  
  173. //          requestLayout();  
  174.         }  
  175.     }  
  176.   
  177.     public boolean isLoading() {  
  178.         return loading;  
  179.     }  
  180.   
  181.     public void setLoading(boolean loading) {  
  182.         this.loading = loading;  
  183.     }  
  184.   
  185.     public void setPorterDuffMode(boolean bool) {  
  186.         porterduffMode = bool;  
  187.     }  


0 0
原创粉丝点击