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%");