編輯:關於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
一直感覺AndroidStudio沒有eclipse快,但是最近由於遇到一個問題不得不將工程遷移到AndroidStudio上,遷移後之前在eclipse上所做的所有批量
在前一章中講的是Android使用HttpURLConnection下載圖片,這一章使用HttpClient下載圖片 HttpURLConnection與HttpClie
一、應用的啟動啟動方式通常來說,在安卓中應用的啟動方式分為兩種:冷啟動和熱啟動。 1、冷啟動:當啟動應用時,後台沒有該應用的進程,這時系統會重新創建一個新的進程分配給該
Android Studio配置及使用OpenCV 前言:最近在做項目移植,項目較大,在Eclipse中配置的Jni及OpenCV環境沒任何問題,但是遷移到St