• [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",内容如下:

    [html] view plaincopy
     
    1. <?xml version="1.0" encoding="UTF-8"?>  
    2. <resources>  
    3.     <!--请参阅  
    4.     [android_sdk]platformsandroid-ndata esvaluesattrs.xml  
    5.     -->  
    6.     <declare-styleable name="porterduff.PorterDuffView">  
    7.         <attr name="porterduffMode" format="boolean"></attr>  
    8.     </declare-styleable>  
    9. </resources>  


    在此声明中,属性名为"porterduffMode",对其赋值范围为boolean型。赋值范围的规范可参考:[android_sdk]platformsandroid-ndata esvaluesattrs.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(0, 0, 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(0, 0, 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.     }  
      188. }  
  • 相关阅读:
    Rocky Linux8国内镜像源
    强制缓存和协商缓存的区别
    从源码来看VUE的执行流程
    plugin
    判断数据类型的方法
    获取函数参数
    BFC
    VUE的$nextTick
    HTTP
    JavaScript创建和触发自定义事件
  • 原文地址:https://www.cnblogs.com/exmyth/p/4628861.html
Copyright © 2020-2023  润新知