Android各種自定義控件

自定義布局簡(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();

? ? ? ? }

? ? });

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末芳悲,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子边坤,更是在濱河造成了極大的恐慌名扛,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惩嘉,死亡現(xiàn)場(chǎng)離奇詭異罢洲,居然都是意外死亡踢故,警方通過(guò)查閱死者的電腦和手機(jī)文黎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)殿较,“玉大人耸峭,你說(shuō)我怎么就攤上這事×芨伲” “怎么了劳闹?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)洽瞬。 經(jīng)常有香客問(wèn)我本涕,道長(zhǎng),這世上最難降的妖魔是什么伙窃? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任菩颖,我火速辦了婚禮,結(jié)果婚禮上为障,老公的妹妹穿的比我還像新娘晦闰。我一直安慰自己放祟,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布呻右。 她就那樣靜靜地躺著跪妥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪声滥。 梳的紋絲不亂的頭發(fā)上眉撵,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音醒串,去河邊找鬼执桌。 笑死,一個(gè)胖子當(dāng)著我的面吹牛芜赌,可吹牛的內(nèi)容都是我干的仰挣。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼缠沈,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼膘壶!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起洲愤,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤颓芭,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后柬赐,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體亡问,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年肛宋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了州藕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡酝陈,死狀恐怖床玻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情沉帮,我是刑警寧澤锈死,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站穆壕,受9級(jí)特大地震影響待牵,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜喇勋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一缨该、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧茄蚯,春花似錦压彭、人聲如沸玻募。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)注祖。三九已至,卻和暖如春询一,著一層夾襖步出監(jiān)牢的瞬間隐孽,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工健蕊, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留菱阵,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓缩功,卻偏偏與公主長(zhǎng)得像晴及,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子嫡锌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容