自定義布局簡(jiǎn)單分類:
一、自繪控件
這里我們實(shí)現(xiàn)一個(gè)可以沿著圓周來(lái)旋轉(zhuǎn)的飛機(jī)圖標(biāo)
首先了解一下PathMeasuer這個(gè)類:
定義:用來(lái)測(cè)量Path的類氏义,可以理解為專門(mén)對(duì)Path每一個(gè)點(diǎn)獲取信息的類
優(yōu)勢(shì):可計(jì)算每一點(diǎn)正切、余切值炭臭,也可截取某一段Path單獨(dú)對(duì)它繪制疹娶;
常用方法:
PathMeasure()初始化只需要new一個(gè)PathMeasure對(duì)象即可,初始化PathMeasure后诬辈,可以通過(guò)PathMeasure.setPath()的方式來(lái)將Path和PathMeasure進(jìn)行綁定
PathMeasure (Path path, boolean forceClosed)path,和setPath()同樣的作用荐吉,forceClosed就是Path最終是否需要閉合焙糟,如果為T(mén)rue的話,則不管關(guān)聯(lián)的Path是否是閉合的样屠,都會(huì)被閉合
getLength獲取計(jì)算的路徑長(zhǎng)度
boolean getSegment (float startD, float stopD, Path dst, boolean startWithMoveTo)截取整個(gè)Path的片段穿撮,通過(guò)參數(shù)startD和stopD來(lái)控制截取的長(zhǎng)度缺脉,并將截取的Path保存到dst中,最后一個(gè)參數(shù)startWithMoveTo表示起始點(diǎn)是否使用moveTo方法悦穿,通常為T(mén)rue攻礼,保證每次截取的Path片段都是正常的、完整的栗柒。
boolean getPosTan (float distance, float[] pos, float[] tan)通過(guò)指定distance(0<distance<getLength)礁扮,來(lái)獲取坐標(biāo)點(diǎn)和切線的坐標(biāo),并保存到pos[]和tan[]數(shù)組中瞬沦。
這里對(duì)我們將用到的方法getPosTan()詳細(xì)圖解一下:
代碼實(shí)現(xiàn):
package com.xxx.uidemo;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.util.AttributeSet;
import android.view.View;
public class RotateView extends View {
? ? private Bitmap bitmap;// 旋轉(zhuǎn)圖標(biāo)
? ? private Path path;//旋轉(zhuǎn)路徑
? ? private PathMeasure pathMeasure;//路徑測(cè)量
? ? private float distanceRatio;// 旋轉(zhuǎn)進(jìn)度
? ? private Paint circlePaint;// 畫(huà)圓圈的畫(huà)筆
? ? private Paint iconPaint;// 畫(huà)圖標(biāo)的畫(huà)筆
? ? private Matrix matrix = new Matrix();// 圖標(biāo)bitmap圖片操作矩陣
? ? public RotateView(Context context) {
? ? ? ? this(context, null);
? ? }
? ? public RotateView(Context context, AttributeSet attrs) {
? ? ? ? this(context, attrs, 0);
? ? }
? ? public RotateView(Context context, AttributeSet attrs, int defStyleAttr) {
? ? ? ? super(context, attrs, defStyleAttr);
? ? ? ? init();
? ? }
? ? private void init() {
? ? ? ? bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.icon_airplane);// 初始化圖標(biāo)
? ? ? ? path = new Path();//初始化path
? ? ? ? path.addCircle(0, 0, 300, Path.Direction.CW);
? ? ? ? pathMeasure = new PathMeasure(path, false);//將圓的path和pathMeasure綁定起來(lái)
? ? ? ? circlePaint = new Paint();
? ? ? ? circlePaint.setColor(Color.BLACK);
? ? ? ? circlePaint.setStyle(Paint.Style.STROKE);
? ? ? ? circlePaint.setStrokeWidth(5);
? ? ? ? circlePaint.setAntiAlias(true);
? ? ? ? iconPaint = new Paint();
? ? ? ? iconPaint.setColor(Color.DKGRAY);
? ? ? ? iconPaint.setStyle(Paint.Style.STROKE);
? ? ? ? iconPaint.setStrokeWidth(2);
? ? }
? ? @Override
? ? protected void onDraw(Canvas canvas) {
? ? ? ? super.onDraw(canvas);
? ? ? ? canvas.drawColor(Color.WHITE);
? ? ? ? int width = canvas.getWidth();
? ? ? ? int height = canvas.getHeight();
? ? ? ? canvas.translate(width / 2, height / 2);
? ? ? ? matrix.reset();
? ? ? ? distanceRatio += 0.006f;
? ? ? ? if (distanceRatio >= 1) {
? ? ? ? ? ? distanceRatio = 0;
? ? ? ? }
? ? ? ? float pos[] = new float[2];// 記錄P點(diǎn)坐標(biāo)
? ? ? ? float tan[] = new float[2];// 記錄P點(diǎn)正切值太伊,tan[0] = cosA ,tan[1] = sinA;
? ? ? ? float distance = pathMeasure.getLength() * distanceRatio;
? ? ? ? //獲取pos和tan值
? ? ? ? pathMeasure.getPosTan(distance, pos, tan);
? ? ? ? //計(jì)算P點(diǎn)要旋轉(zhuǎn)的角度
? ? ? ? float degree = (float) (Math.atan2(tan[1], tan[0]) * 180 / Math.PI);//計(jì)算角度使用Math.atan2(tan[1], tan[0]),然后再轉(zhuǎn)化為真正意義上的角度
? ? ? ? matrix.postRotate(degree, bitmap.getWidth() / 2, bitmap.getHeight() / 2);// 設(shè)置旋轉(zhuǎn)角度
? ? ? ? matrix.postTranslate(pos[0] - bitmap.getWidth() / 2, pos[1] - bitmap.getHeight() / 2);// 設(shè)置圖標(biāo)中心點(diǎn)逛钻,確保在圓周軌跡上
? ? ? ? canvas.drawPath(path, circlePaint);//畫(huà)圓形
? ? ? ? canvas.drawBitmap(bitmap, matrix, iconPaint);//畫(huà)圖標(biāo)
? ? ? ? invalidate();
? ? }
}
展示效果:
二僚焦、組合控件
這里我們實(shí)現(xiàn)一個(gè)應(yīng)用菜單欄,相當(dāng)于ToolBar的作用
由于比較簡(jiǎn)單曙痘,這里我們直接貼代碼:
首先自定義實(shí)現(xiàn)ToolBar
package com.xxx.uidemo;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
public class ToolBar extends RelativeLayout {
? ? private ImageView ivBack;
? ? private ImageView ivMenu;
? ? private ImageView ivMore;
? ? private TextView tvTitle;
? ? private int titleColor = Color.BLACK;
? ? private String title;
? ? public ToolBar(Context context) {
? ? ? ? this(context, null);
? ? }
? ? public ToolBar(Context context, AttributeSet attrs) {
? ? ? ? this(context, attrs, 0);
? ? }
? ? public ToolBar(Context context, AttributeSet attrs, int defStyleAttr) {
? ? ? ? super(context, attrs, defStyleAttr);
? ? ? ? init(context, attrs);
? ? }
? ? private void init(Context context, AttributeSet attrs) {
? ? ? ? TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ToolBar);
? ? ? ? titleColor = typedArray.getColor(R.styleable.ToolBar_titleColor, Color.BLACK);
? ? ? ? title = typedArray.getString(R.styleable.ToolBar_title);
? ? ? ? typedArray.recycle();
? ? ? ? LayoutInflater.from(context).inflate(R.layout.tool_bar, this, true);
? ? ? ? ivBack = findViewById(R.id.iv_back);
? ? ? ? ivMenu = findViewById(R.id.iv_menu);
? ? ? ? ivMore = findViewById(R.id.iv_more);
? ? ? ? tvTitle = findViewById(R.id.tv_title);
? ? ? ? setTitle(titleColor, title);
? ? }
? ? public void setBackClickListener(OnClickListener onClickListener) {
? ? ? ? ivBack.setOnClickListener(onClickListener);
? ? }
? ? public void setMenuClickListener(OnClickListener onClickListener) {
? ? ? ? ivMenu.setOnClickListener(onClickListener);
? ? }
? ? public void setMoreClickListener(OnClickListener onClickListener) {
? ? ? ? ivMore.setOnClickListener(onClickListener);
? ? }
? ? private void setTitle(int color, String title) {
? ? ? ? tvTitle.setTextColor(color);
? ? ? ? if (!TextUtils.isEmpty(title)) {
? ? ? ? ? ? tvTitle.setText(title);
? ? ? ? }
? ? }
}
自定義屬性文件attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
? ? <declare-styleable name="ToolBar">
? ? ? ? <attr name="titleColor" format="color"></attr>
? ? ? ? <attr name="title" format="string"></attr>
? ? </declare-styleable>
</resources>
ToolBar的布局文件tool_bar.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
? ? android:layout_width="match_parent"
? ? android:layout_height="50dp">
? ? <ImageView
? ? ? ? android:id="@+id/iv_back"
? ? ? ? android:layout_width="wrap_content"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? android:layout_alignParentLeft="true"
? ? ? ? android:layout_centerVertical="true"
? ? ? ? android:layout_marginLeft="2dp"
? ? ? ? android:padding="5dp"
? ? ? ? android:src="@mipmap/back_icon" />
? ? <ImageView
? ? ? ? android:id="@+id/iv_menu"
? ? ? ? android:layout_width="wrap_content"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? android:layout_centerVertical="true"
? ? ? ? android:layout_toRightOf="@id/iv_back"
? ? ? ? android:padding="5dp"
? ? ? ? android:src="@mipmap/menu_icon" />
? ? <TextView
? ? ? ? android:id="@+id/tv_title"
? ? ? ? android:layout_width="wrap_content"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? android:layout_centerHorizontal="true"
? ? ? ? android:layout_centerVertical="true"
? ? ? ? android:text="標(biāo)題"
? ? ? ? android:textSize="22sp" />
? ? <ImageView
? ? ? ? android:id="@+id/iv_more"
? ? ? ? android:layout_width="wrap_content"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? android:layout_alignParentRight="true"
? ? ? ? android:layout_centerVertical="true"
? ? ? ? android:layout_marginRight="2dp"
? ? ? ? android:padding="5dp"
? ? ? ? android:src="@mipmap/more_icon" />
</RelativeLayout>
接下來(lái)就是使用啦
activity_test.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
? ? xmlns:app="http://schemas.android.com/apk/res-auto"
? ? android:layout_width="match_parent"
? ? android:layout_height="match_parent">
? ? <com.xxx.uidemo.ToolBar
? ? ? ? android:id="@+id/tb_bar"
? ? ? ? android:layout_width="match_parent"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? app:title="首頁(yè)展示"
? ? ? ? app:titleColor="@color/colorPrimaryDark"></com.yan.uidemo.ToolBar>
</RelativeLayout>
在onCreate生命周期中使用
@Override
protected void onCreate(Bundle savedInstanceState) {
? ? super.onCreate(savedInstanceState);
? ? setContentView(R.layout.activity_test);
? ? ToolBar toolBar = findViewById(R.id.tb_bar);
? ? toolBar.setBackClickListener(new View.OnClickListener() {
? ? ? ? @Override
? ? ? ? public void onClick(View view) {
? ? ? ? ? ? Toast.makeText(MainActivity.this, "點(diǎn)擊返回", Toast.LENGTH_SHORT).show();
? ? ? ? }
? ? });
? ? toolBar.setMenuClickListener(new View.OnClickListener() {
? ? ? ? @Override
? ? ? ? public void onClick(View view) {
? ? ? ? ? ? Toast.makeText(MainActivity.this, "點(diǎn)擊菜單", Toast.LENGTH_SHORT).show();
? ? ? ? }
? ? });
? ? toolBar.setMoreClickListener(new View.OnClickListener() {
? ? ? ? @Override
? ? ? ? public void onClick(View view) {
? ? ? ? ? ? Toast.makeText(MainActivity.this, "點(diǎn)擊更多", Toast.LENGTH_SHORT).show();
? ? ? ? }
? ? });
}