Android類庫爲我們提供了非常豐富的控件來美化我們的UI界面。也提供了非常方式的拓展方法讓我們設計自己風格的控件(自定義控件)。
要想自定義控件首先要先熟悉幾個方法:
1,onFinishInflate(),從xml文件中加載組件後回調。
2,onSizeChange(),組件大小改變時回調。
3,onMeasure(),回調該方法會組件的大小進行測量。
4,onLayout(),回調該方法確定組件顯示的位置。
5,onDraw(),回調該方法對組件進行繪製。
6,onTouchEvent(),監聽觸摸事件回調。
(1-5的方法的回調通常順序爲:onFinishInflate ,onMeasure,onSizeChange,onLayout,onDraw)
自定義控件方法有三種:
1,對已有控件的擴展,
2,組合已有控件形成新的控件。
3,繼承View形成全新的控件。
接下來會對每一種方法舉例說明(第一對TextView的擴展,多層背景+文字閃爍效果;第二組合已有控件形成統一的TopBar,
第三,圖文比例圖),怕麻煩放在一個界面展示了。
1,對已有控件的擴展:我們繼承TextView,我們想要TextView的背景更加的豐富,給其多繪製幾層背景,並且文字帶閃動效果。
package com.example.songbinwang.littledemo.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.TextView;
/**
* on 2016/5/4.
*/
public class MyTextView extends TextView{
public static final String Tag = "wangMyText";
private Paint mPaint1,mPaint2;
private LinearGradient mLinearGradient;
private Matrix mLinearMatrix;
private int mMeasuredWidth;
private int mMeasuredHeight;
private Paint mPaint;
private int mTranslate;
public MyTextView(Context context){
this(context, null);
}
public MyTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mPaint1 = new Paint();
mPaint1.setColor(Color.LTGRAY);
mPaint1.setStyle(Paint.Style.FILL);
mPaint2 = new Paint();
mPaint2.setColor(Color.YELLOW);
mPaint2.setStyle(Paint.Style.FILL);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
Log.i(Tag,"onFinishInflate");
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w,h,oldw,oldh);
Log.i(Tag, "onSizeChanged");
if(mLinearGradient == null){
mMeasuredWidth = getMeasuredWidth();
mPaint = getPaint();
if(mMeasuredWidth > 0){
mLinearGradient = new LinearGradient(
0,0,mMeasuredWidth, 0,new int[]{Color.RED,Color.WHITE,Color.RED},null, Shader.TileMode.CLAMP
);
mPaint.setShader(mLinearGradient);
mLinearMatrix = new Matrix();
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Log.i(Tag, "onMeasure");
mMeasuredWidth = getMeasuredWidth();
mMeasuredHeight = getMeasuredHeight();
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
Log.i(Tag, "onLayout");
}
@Override
protected void onDraw(Canvas canvas) {
Log.i(Tag, "onDraw");
canvas.drawRect(0, 0, mMeasuredWidth, mMeasuredHeight, mPaint1);
canvas.drawRect(getPaddingLeft(), getPaddingTop(), getMeasuredWidth() - getPaddingRight(), getMeasuredHeight() - getPaddingBottom(), mPaint2);
super.onDraw(canvas);
if(mLinearGradient != null) {
mTranslate += mMeasuredWidth / 5;
if (mTranslate > 2 * mMeasuredWidth) {
mTranslate = -mMeasuredWidth;
}
mLinearMatrix.setTranslate(mTranslate, 0);
mLinearGradient.setLocalMatrix(mLinearMatrix);
postInvalidateDelayed(100);
}
}
}
2,組合控件形成統一樣式的標題欄TopBar;
2,1屬性定義:value/attr.xml
<declare-styleable name="TopBar">
<attr name="title_text" format="string"></attr>
</declare-styleable>
2,2代碼實現:
package com.example.songbinwang.littledemo.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.example.songbinwang.littledemo.R;
/**
* Created by songbinwang on 2016/5/4.
*/
public class TopBar extends RelativeLayout{
private Button mRightBtn;
private ImageView mLeftImg, mRightImg;
private TextView mTitleTv;
private String mTitle_Text;
public static final String Tag = "wangTopbar";
public TopBar(Context context){
this(context, null);
}
public TopBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TopBar(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public TopBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initFromAttribute(context, attrs, defStyleAttr, defStyleRes);
addMyChildView(context);
registerListener();
}
private void initFromAttribute(Context context,AttributeSet attrs,int defStyleAttr, int defStyleRes){
TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.TopBar, defStyleAttr,defStyleRes);
mTitle_Text = mTypedArray.getString(R.styleable.TopBar_title_text);//獲取屬性值
mTypedArray.recycle();
};
private void addMyChildView(Context context){
mTitleTv = new TextView(context);
mTitleTv.setTextColor(Color.WHITE);
mTitleTv.setText(mTitle_Text);
mTitleTv.setTextSize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 5, context.getResources().getDisplayMetrics()));
LayoutParams mTitleLp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
mTitleLp.addRule(RelativeLayout.CENTER_IN_PARENT);
addView(mTitleTv, mTitleLp);
mLeftImg = new ImageView(context);
mLeftImg.setImageResource(R.drawable.action_bar_back_icon);
LayoutParams mLeftImgLp = new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.MATCH_PARENT);
mLeftImgLp.leftMargin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, context.getResources().getDisplayMetrics());
mLeftImgLp.addRule(RelativeLayout.ALIGN_PARENT_LEFT | RelativeLayout.CENTER_VERTICAL);
addView(mLeftImg, mLeftImgLp);
mRightBtn = new Button(context);
mRightBtn.setTextSize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 5, context.getResources().getDisplayMetrics()));
mRightBtn.setText("分享");
mRightBtn.setTextColor(Color.WHITE);
mRightBtn.setBackgroundColor(Color.TRANSPARENT);
LayoutParams mRightLp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
mRightLp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
mRightLp.addRule(RelativeLayout.CENTER_VERTICAL);
addView(mRightBtn, mRightLp);
mRightBtn.setVisibility(View.GONE);
mRightImg = new ImageView(context);
mRightImg.setImageResource(R.drawable.actionbar_more_img);
LayoutParams mRightImgLp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
mRightImgLp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
mRightImgLp.addRule(RelativeLayout.CENTER_VERTICAL);
mRightImgLp.rightMargin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, context.getResources().getDisplayMetrics());
addView(mRightImg, mRightImgLp);
mRightImg.setVisibility(View.GONE);
}
private void registerListener(){
mLeftImg.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
if(mOnTopbarClickListener != null){
mOnTopbarClickListener.onLeftClick(v);
}
}
});
mRightBtn.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
if(mOnTopbarClickListener != null){
mOnTopbarClickListener.onRightClick(v);
}
}
});
mRightImg.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
if(mOnTopbarClickListener != null){
mOnTopbarClickListener.onRightClick(v);
}
}
});
}
public void setRightBtnVisibility(int visibility){
mRightBtn.setVisibility(visibility);
}
public void setRightImgVisibility(int visibility){
mRightImg.setVisibility(visibility);
}
public Button getRightBtn(){
return mRightBtn;
}
public ImageView getmRightImg(){
return mRightImg;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
this.setBackgroundColor(getResources().getColor(android.R.color.holo_blue_light));
}
OnTopbarClickListener mOnTopbarClickListener;
public void setOnTopbarClickListener(OnTopbarClickListener mOnTopbarClickListener){
this.mOnTopbarClickListener = mOnTopbarClickListener;
}
public interface OnTopbarClickListener{
public void onLeftClick(View view);
public void onRightClick(View view);
}
}
3,3 實際應用:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical"
tools:context=".MainActivity">
<com.example.songbinwang.littledemo.view.TopBar
android:id="@+id/topbar"
android:layout_width="match_parent"
android:layout_height="50dp"
app:title_text="自定義title" />
<include layout="@layout/content_main" />
<android.support.design.widget.FloatingActionButton android:id="@+id/fab"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin"
android:src="@android:drawable/ic_dialog_email" />
</LinearLayout>
mTopbar = (TopBar)findViewById(R.id.topbar);
mTopbar.setRightBtnVisibility(View.VISIBLE);
mTopbar.setOnTopbarClickListener(new TopBar.OnTopbarClickListener() {
@Override
public void onLeftClick(View view) {
Toast.makeText(MainActivity.this,"left", Toast.LENGTH_LONG).show();
}
@Override
public void onRightClick(View view) {
Toast.makeText(MainActivity.this,"right", Toast.LENGTH_LONG).show();
}
});
3,繼承View形成的新的控件。圓+弧形比例圖。
3,1屬性定義:values/attr.xml
<declare-styleable name="CircleRatioView">
<attr name="circle_ratio_title" format="string"></attr>
<attr name="ratio" format="float"></attr>
</declare-styleable>
3,2代碼實現
package com.example.songbinwang.littledemo.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import com.example.songbinwang.littledemo.R;
/**
* Created by songbinwang on 2016/5/16.
*/
public class CircleRatioView extends View {
private String title = "title";
private float ratio = 0.5f;
private Paint mPaint;
private Rect mTextBound;
private int mArcWidth;
private int mCirclePadding;
private int distanceBetweenCircleAndArc;
public CircleRatioView(Context context){
this(context, null);
}
public CircleRatioView(Context context, @Nullable AttributeSet attrs){
this(context, attrs, 0);
}
public CircleRatioView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public CircleRatioView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes){
super(context, attrs, defStyleAttr, defStyleRes);
TypedArray mTypeArray = context.obtainStyledAttributes(attrs, R.styleable.CircleRatioView,defStyleAttr,defStyleRes);
int length = mTypeArray.length();
for(int i=0;i<length;i++){
int attr = mTypeArray.getIndex(i);
switch(attr){
case R.styleable.CircleRatioView_ratio:
ratio = mTypeArray.getFloat(R.styleable.CircleRatioView_ratio,0.5f);
break;
case R.styleable.CircleRatioView_circle_ratio_title:
title = mTypeArray.getString(R.styleable.CircleRatioView_circle_ratio_title);
break;
}
}
mTypeArray.recycle();
mArcWidth = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20.0f, context.getResources().getDisplayMetrics());
mCirclePadding = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20.0f, context.getResources().getDisplayMetrics());
distanceBetweenCircleAndArc = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20.0f, context.getResources().getDisplayMetrics());
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.BLUE);
mPaint.setTextSize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 14, context.getResources().getDisplayMetrics()));
mTextBound = new Rect();
mPaint.getTextBounds(title, 0, title.length(), mTextBound);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = measureWidth(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec);
setMeasuredDimension(width, height);
}
private int measureWidth(int widthMeasureSpec){
int width = 0;
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
if(widthMode == MeasureSpec.EXACTLY){
width = widthSize;
}else if(widthMode == MeasureSpec.AT_MOST){
width = mTextBound.width() + (mArcWidth + distanceBetweenCircleAndArc + mCirclePadding)* 2;
}
return width;
};
private int measureHeight(int heightMeasureSpec){
int height = 0;
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if(heightMode == MeasureSpec.EXACTLY){
height = heightSize;
}else if(heightMode == MeasureSpec.AT_MOST){
height = mTextBound.height() + (mArcWidth + distanceBetweenCircleAndArc + mCirclePadding) * 2;
}
return height;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
mPaint.reset();
mPaint.setColor(getResources().getColor(android.R.color.holo_green_light));
mPaint.setStyle(Paint.Style.FILL);
int radius = width/2 - mArcWidth - distanceBetweenCircleAndArc;
canvas.drawCircle(width / 2, height / 2, radius, mPaint);
RectF rectArc = new RectF(0.1f*width ,0.1f*height, 0.9f*width,0.9f*height);
mPaint.reset();
mPaint.setColor(getResources().getColor(android.R.color.holo_green_light));
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mArcWidth);
canvas.drawArc(rectArc, -90, (int) 360 * ratio, false, mPaint);
mPaint.reset();
mPaint.setColor(Color.WHITE);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setAntiAlias(true);
mPaint.setTextSize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 14, getResources().getDisplayMetrics()));
int distanceX = width/2 - mTextBound.width()/2;
int distanceY = height/2 - mTextBound.height()/2 + mTextBound.height()/2;
canvas.save();
canvas.translate(distanceX,distanceY);
canvas.drawText(title, 0, title.length(),0,0,mPaint);
canvas.restore();
}
public void setRatio(float ratio, String title){
this.ratio = ratio;
this.title = title;
mPaint.reset();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.BLUE);
mPaint.setTextSize((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 14, getResources().getDisplayMetrics()));
mTextBound = new Rect();
mPaint.getTextBounds(title, 0, title.length(), mTextBound);
invalidate();
}
}
3,3具體應用:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:showIn="@layout/activity_main"
tools:context=".MainActivity">
<com.example.songbinwang.littledemo.view.MyTextView
android:id="@+id/tv_wang"
android:layout_marginTop="20dp"
android:padding="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World"/>
<com.example.songbinwang.littledemo.view.CircleRatioView
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_marginTop="20dp"
android:layout_below="@+id/tv_wang"
app:ratio="0.6"
app:circle_ratio_title="hello world"/>
<Button
android:id="@+id/btn_pm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:text="packageManager"/>
</RelativeLayout>
ratioView = (CircleRatioView) findViewById(R.id.ratioview);
ratioView.setRatio(0.8f,"同比增長80%");