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

  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下的布局文件,需首先在原有基础上添加一命名空间,代码如下:

  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如下:

  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 的规则显示图片。
由效果图可知,需要生成一半透明的前景图。
生成前景图的代码为:

  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()方法中,需要根据加载进度,循环滴绘制叠加区域。代码如下:

  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请看官自行实现:

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

[Android] PorterDuff使用实例----实现新浪微博图片下载效果的更多相关文章

  1. Android:通过滤镜实现点击图片变暗效果

    实现点击图片(ImageView)变暗效果,有一个较简单的方法,就是讲目标图片设置为背景图片(setBackground),再创建一个selector.xml文件,里面放置一张普通状态时的透明图片,一 ...

  2. Android 自定义 ViewPager 打造千变万化的图片切换效果

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38026503 记得第一次见到ViewPager这个控件,瞬间爱不释手,做东西的主 ...

  3. Android 轮播图Banner切换图片的效果

    Android XBanner使用详解 2018年03月14日 08:19:59 AND_Devil 阅读数:910   版权声明:本文为博主原创文章,未经博主允许不得转载. https://www. ...

  4. Android 自定义 HorizontalScrollView 打造再多图片(控件)也不怕 OOM 的横向滑动效果

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38140505 自从Gallery被谷歌废弃以后,Google推荐使用ViewPa ...

  5. picasso-强大的Android图片下载缓存库

    编辑推荐:稀土掘金,这是一个针对技术开发者的一个应用,你可以在掘金上获取最新最优质的技术干货,不仅仅是Android知识.前端.后端以至于产品和设计都有涉猎,想成为全栈工程师的朋友不要错过! pica ...

  6. picasso_强大的Android图片下载缓存库

    tag: android pic skill date: 2016/07/09 title: picasso-强大的Android图片下载缓存库 [本文转载自:泡在网上的日子 参考:http://bl ...

  7. Picasso – Android系统的图片下载和缓存类库

    Picasso – Android系统的图片下载和缓存类库 Picasso 是Square开源的一个用于Android系统下载和缓存图片的项目.该项目和其他一些下载图片项目的主要区别之一是:使用4.0 ...

  8. 毕加索的艺术——Picasso,一个强大的Android图片下载缓存库,OkHttpUtils的使用,二次封装PicassoUtils实现微信精选

    毕加索的艺术--Picasso,一个强大的Android图片下载缓存库,OkHttpUtils的使用,二次封装PicassoUtils实现微信精选 官网: http://square.github.i ...

  9. Android性能优化-减小图片下载大小

    原文链接 https://developer.android.com/topic/performance/network-xfer.html 内容概要 理解图片的格式 PNG JPG WebP 如何选 ...

随机推荐

  1. Autofac 一个使用Demo

    一:接口 二:实现: 三:调用: 首先上图: 一:接口代码 public interface IPersonDa { PersonEntity Get(int id); } 二:实现 public c ...

  2. mongodb查询之模糊查询

    mongodb的模糊查询是用正则表达式来实现的.例子如下: db.COMMODITY_COMMODITY_SHOP.find({name:{$regex :/南京/i}})

  3. .net中Web.config文件的基本原理及相关设置

    11.7  使用web.config配置文件 Web配置文件web.config是Web 应用程序的数据设定文件,它是一份 XML 文件,内含 Web 应用程序相关设定的 XML 标记,可以用来简化  ...

  4. minus的用法

    简单的说就是去同留异. MINUS 指令是运用在两个 SQL 语句上.它先找出第一个 SQL 语句所产生的结果,然后看这些结果有没有在第二个 SQL 语句的结果中.如果有的话,那这一笔资料就被去除,而 ...

  5. deep learning in nlp 资料文献

    Deep Learning for Natural Language Processing (without Magic) http://nlp.stanford.edu/courses/NAACL2 ...

  6. PHP 杂项 函数

    安装 杂项函数是 PHP 核心的组成部分.无需安装即可使用这些函数. Runtime 配置 杂项函数的行为受 php.ini 文件中的设置的影响. 杂项配置选项: 名称 默认 描述 可更改 ignor ...

  7. sqlalchemy--group_concat的使用

    今天,一个app客户端同事需要我服务器端提供一组数据,这组数据要按类分好,把整个结构都展示给他,他直接使用就可以了.数据大概如下面这种: 同事需要的结构大概就是类型1有多少,分别是什么;类型2有多少, ...

  8. 前端MVC学习笔记(一)——MVC概要与angular概要、模板与数据绑定

    一.前端MVC概要 1.1.库与框架的区别 框架是一个软件的半成品,在全局范围内给了大的约束.库是工具,在单点上给我们提供功能.框架是依赖库的.AngularJS是框架而jQuery则是库. 1.2. ...

  9. oldboy第四天学习

    一.感觉上课没有太多的知识.也可以去理解.但是作业太难了... 二.hash() #python里面的哈希类型是在一个程序中不变,如果换了python 哈希是不#一样的. #字典为什么快,因为他把字典 ...

  10. 详解ios文件系统文件目录读写操作-备用

    iPhone文件读写系统操作教程是本文要介绍的内容,对于一个运行在iPhone得app,它只能访问自己根目录下得一些文件(所谓sandbox).一个app发布到iPhone上后,它得目录结构如下:  ...