Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> PorterDuff使用實例----實現新浪微博圖片下載效果

PorterDuff使用實例----實現新浪微博圖片下載效果

編輯:關於Android編程

先上效果圖,如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] 
<?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下的布局文件,需首先在原有基礎上添加一命名空間,代碼如下:
[html] 
<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如下:
[html]
<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 的規則顯示圖片。
由效果圖可知,需要生成一半透明的前景圖。
生成前景圖的代碼為:
[java] 
/** 生成一寬與背景圖片等同高為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()方法中,需要根據加載進度,循環滴繪制疊加區域。代碼如下:
[java] view plaincopy
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請看官自行實現:
[java] 
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) { 
    } 

[java] 
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:[email protected]
 * @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; 
    } 

 


[java] 
作者:sodino
 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved