先上效果图,如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. css ie hack整理

    网上有很多关于ie hack的文章,可能由于文章发布后ie的版本还在升级.所以导致有些hack写法已经不适用了.以下是本人整理的ie6-11的一些hack常用写法.(以下默认文档模式为标准模式) 1. ...

  2. MFC 全局配置 读取保存配置

    不知道关于全局配置别人都是怎么处理的,最近做的东西都用到全局配置,而且要保存软件的设置,下次启动时要使用上次关闭时的配置. 我的做法是建一个类用来保存和读取配置,并且在这个类中创建一些变量,供所有的界 ...

  3. [转]《深度探索C++对象模型》读书笔记[一]

    前 言 Stanley B.Lippman1.   任何对象模型都需要的三种转换风味: ü        与编译器息息相关的转换 ü        语言语义转换 ü        程序代码和对象模型的 ...

  4. uva 12207 - That is Your Queue

    #include <cstdio> #include <iostream> #include <deque> using namespace std; int ma ...

  5. MySql中的事务嵌套

    1.Mysql中的事务必须是InnoDB.Berkeley DB引擎,myisam不支持. 2.Mysql是不支持嵌套事务的,开启了一个事务的情况下,再开启一个事务,会隐式的提交上一个事务. 3.My ...

  6. CentOS+nginx+uwsgi+Python 多站点环境搭建

    转载:http://www.cnblogs.com/xiongpq/p/3381069.html 环境: CentOS X64 6.5 nginx 1.5.6 Python 2.7.5 正文: 一:安 ...

  7. 转载:JSONObject.fromObject(map)(JSON与JAVA数据的转换)

    转载网址:http://blog.sina.com.cn/s/blog_821025b70100wh6v.html JSON与JAVA数据的转换(JSON 即 JavaScript Object Na ...

  8. jquery判断滚动条到底

    $(document).scroll(function(){ var dHeight = $(document).height(); var wHeight = $(window).height(); ...

  9. CentOS下建立本地YUM源并自动更新

    1. 尽管有很多的免费镜像提供yum源服务,但是还是有必要建立自己的yum服务器,主要出于以下几点考虑: l 网络速度:访问互联网可能比较慢 l 节省带宽:如果有大量的服务器,架设自己的yum源可以有 ...

  10. Python----Tornado安装

    Tornado安装,环境准备:          1.python安装包及安装 2.Tornado安装包 Python包安装 Linux下安装 如果使用的是 Linux系统 或 Mac OS X ,系 ...