距離上次寫博客已經記不清楚是多久啦!
就在今天16年7月1號一個好基友突然說項目急用一個變色的loading,
剛好下午不是很忙就決定把這個逼給裝了,寫出來看看效果。
效果如下:
以前只看到IOS有這種效果,安卓好像挺難實現的,反正我是不知道怎么實現!!
想了一會覺得LinearGradient可以實現這種效。
好基友就說用bitmapshader也可以實現實現,我覺得這兩種都太麻煩啦!!
靈機一動 google一下發現 Canvas中的clipRect(float left, float top, float right, float bottom, Paint paint)方法是在手機屏幕上裁剪出一塊區域來,起點是從屏幕的左上角開始。
好吧,其實我也是參考了別人的博客才知道這個方法是干嘛的。
我們都知道 在項目中的多個界面使用我自定義的View,每個界面該自定義View的顏色都不相同,這時候如果沒有自定義屬性,那我們是不是需要構建不同顏色的View出來呢,這樣子我們的代碼就會顯得很沉厄,所以這時候我們就需要自定義其屬性來滿足我們不同的需求,自定義屬性呢,我們需要在values下建立attrs.xml文件,在其中定義我們需要定義的屬性,然后在自定義View中也要做相對應的修改。
在values下面新建一個attrs.xml,現在里面定義我們的屬性。
接下來我貼上我在自定義這個進度條所用到的屬性:
<declare-styleable name="loadingView">
<attr name="text" format="string" />
<attr name="text_size" format="dimension" />
<attr name="text_origin_color" format="color|reference" />
<attr name="text_change_color" format="color|reference" />
<attr name="bgd_change_color" format="color|reference" />
<attr name="bgd_origin_color" format="color|reference" />
<attr name="progress_color" format="color|reference" />
<attr name="progress_max" format="integer" />
<attr name="progress" format="integer" />
</declare-styleable>
自定義View的屬性我們算是定義好了,接下來就是怎么獲取屬性和代碼的編寫了,我們需要在構造方法中獲取我們自己定義的相關屬性,我們先調用context.obtainStyledAttributes(attrs, R.styleable.loadingView)來獲取TypedArray,然后從TypedArray獲取我們定義的屬性:
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.loadingView);
mText = ta.getString(R.styleable.loadingView_text);
mTextSize = ta.getDimensionPixelSize(R.styleable.loadingView_text_size, mTextSize);
bgd_Origin_color =ta.getColor(R.styleable.loadingView_bgd_origin_color, bgd_Origin_color);
bgd_Change_color = ta.getColor(R.styleable.loadingView_bgd_change_color, bgd_Change_color);
mTextOriginColor =ta.getColor(R.styleable.loadingView_text_origin_color, mTextOriginColor);
mTextChangeColor =ta.getColor(R.styleable.loadingView_text_change_color, mTextChangeColor);
mProgress = ta.getInt(R.styleable.loadingView_progress, mProgress);
mProgress_max = ta.getInt(R.styleable.loadingView_progress_max,mProgress_max);
ta.recycle();
為了方便大家理解,我將自定義View的全部代碼貼出來,看注釋就好:
/**
* Created by Liqingwen on 7/1/16.
* Email: Liqingqingqingwen@163.com
*/
public class LoadingView extends View {
// 字體內容
private String mText = "正在下載";
// 進度背景
private int bgd_Change_color;
// 默認背景
private int bgd_Origin_color;
// 字體大小
private int mTextSize = sp2px(18);
//默認字體顏色
private int mTextOriginColor;
//進度字體顏色
private int mTextChangeColor;
//當前進度
private int mProgress = 0;
//進度最大值
private int mProgress_max;
// 畫筆對象的引用
private Paint mPaint;
// 測量以后的寬
private int measuredWidth;
// 測量以后的高
private int measuredHeight;
public LoadingView(Context context) {
super(context);
}
public LoadingView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.loadingView);
mText = ta.getString(R.styleable.loadingView_text);
mTextSize = ta.getDimensionPixelSize(R.styleable.loadingView_text_size, mTextSize);
bgd_Origin_color = ta.getColor(R.styleable.loadingView_bgd_origin_color, bgd_Origin_color);
bgd_Change_color = ta.getColor(R.styleable.loadingView_bgd_change_color, bgd_Change_color);
mTextOriginColor = ta.getColor(R.styleable.loadingView_text_origin_color, mTextOriginColor);
mTextChangeColor = ta.getColor(R.styleable.loadingView_text_change_color, mTextChangeColor);
mProgress = ta.getInt(R.styleable.loadingView_progress, mProgress);
mProgress_max = ta.getInt(R.styleable.loadingView_progress_max,mProgress_max);
ta.recycle(); //加載完成回收資源
mPaint.setAntiAlias(true); //消除鋸齒
mPaint.setStyle(Paint.Style.FILL); //設置空心
mPaint.setTextSize(mTextSize);// 設置字體大小
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
measuredWidth = getMeasuredWidth();
measuredHeight = getMeasuredHeight();
float widthFloat = ((float)measuredWidth) / ((float) mProgress_max) ; // 通過寬度除以 最大進度 得到寬的倍數
drawText(canvas,mTextOriginColor,bgd_Origin_color,0,measuredWidth); // 默認
drawText(canvas,mTextChangeColor,bgd_Change_color,widthFloat * mProgress ,measuredWidth); //進度條 將寬的倍數 乘以當前進度 這樣就可以把進度覆蓋整個View widthFloat * mProgress
}
protected void drawText(Canvas canvas , int color ,int bgdColor, float startX , int endX){
RectF mRect = new RectF();
mRect.top = 0;
mRect.bottom = measuredWidth;
mRect.left = startX;
mRect.right = endX;
mPaint.setColor(bgdColor);//設置背景顏色
canvas.drawRect(mRect,mPaint); // 通過矩形畫背景
canvas.save();
/*
* 這里canvas.save();和canvas.restore();是兩個相互匹配出現的,作用是用來保存畫布的狀態和取出保存的狀態的。這里稍微解釋一下,
* 當我們對畫布進行旋轉,縮放,平移等操作的時候其實我們是想對特定的元素進行操作,比如圖片,一個矩形等,
* 但是當你用canvas的方法來進行這些操作的時候,其實是對整個畫布進行了操作,那么之后在畫布上的元素都會受到影響,
* 所以我們在操作之前調用canvas.save()來保存畫布當前的狀態,當操作之后取出之前保存過的狀態,這樣就不會對其他的元素進行影響。
*/
mPaint.setColor(color); //字體顏色
canvas.clipRect(startX, 0, endX, measuredHeight);// 剪輯矩形
int percent = (int)(((float)mProgress / (float)mProgress_max) * 100); //中間的進度百分比,先轉換成float在進行除法運算,不然都為0
float textWidth = mPaint.measureText(mText + percent + "%");// 測量當前字體寬度
//畫出百分比
canvas.drawText(mText + percent + "%", (measuredWidth / 2) - (textWidth / 2) , measuredHeight / 2 - ((mPaint.descent() + mPaint.ascent()) / 2), mPaint);
canvas.restore();
}
public void setmProgress(int mProgress) {
this.mProgress = mProgress;
invalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = measureWidth(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec);
setMeasuredDimension(width, height);
}
private int measureHeight(int measureSpec) {
int mode = MeasureSpec.getMode(measureSpec);
int val = MeasureSpec.getSize(measureSpec);
int result = 0;
switch (mode)
{
case MeasureSpec.EXACTLY:
result = val;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.UNSPECIFIED:
result = val;
break;
}
result = mode == MeasureSpec.AT_MOST ? Math.min(result, val) : result;
return result + getPaddingTop() + getPaddingBottom();
}
private int measureWidth(int measureSpec) {
int mode = MeasureSpec.getMode(measureSpec);
int val = MeasureSpec.getSize(measureSpec);
int result = 0;
switch (mode)
{
case MeasureSpec.EXACTLY:
result = val;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.UNSPECIFIED:
result = val;
break;
}
result = mode == MeasureSpec.AT_MOST ? Math.min(result, val) : result;
return result + getPaddingLeft() + getPaddingRight();
}
private int sp2px(float dpVal) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
dpVal, getResources().getDisplayMetrics());
}
}
這里引用csdn一位大神的博客:
http://blog.csdn.net/harvic880925/article/details/39080931/
關于裁剪畫布(clip系列函數)
裁剪畫布是利用Clip系列函數,通過與Rect、Path、Region取交、并、差等集合運算來獲得最新的畫布形狀。除了調用Save、Restore函數以外,這個操作是不可逆的,一但Canvas畫布被裁剪,就不能再被恢復!Clip系列函數如下:
boolean clipPath(Path path)
boolean clipPath(Path path, Region.Op op)
boolean clipRect(Rect rect, Region.Op op)
boolean clipRect(RectF rect, Region.Op op)
boolean clipRect(int left, int top, int right, int bottom)
boolean clipRect(float left, float top, float right, float bottom)
boolean clipRect(RectF rect)
boolean clipRect(float left, float top, float right, float bottom, Region.Op op)
boolean clipRect(Rect rect)
boolean clipRegion(Region region)
boolean clipRegion(Region region, Region.Op op)
以上就是根據Rect、Path、Region來取得最新畫布的函數,難度都不大,就不再一一講述。利用ClipRect()來稍微一講
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
canvas.drawColor(Color.RED);
canvas.clipRect(new Rect(100, 100, 200, 200));
canvas.drawColor(Color.GREEN);
}
先把背景色整個涂成紅色。顯示在屏幕上然后裁切畫布,最后最新的畫布整個涂成綠色。可見綠色部分,只有一小塊,而不再是整個屏幕了。關于兩個畫布與屏幕合成,我就不再畫圖了,跟上面的合成過程是一樣的。
效果圖:
整體效果圖:
好啦,這個逼我裝完啦。
如果有什么不清楚的地方記得留言。