日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android自定义view详解,使用实例,自定义属性,贝塞尔曲线

發布時間:2023/12/10 Android 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android自定义view详解,使用实例,自定义属性,贝塞尔曲线 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

//只會觸發執行onDraw方法,只會改變繪制里面的內容,條目的繪制
invalidate();?

?//只會觸發執行onDraw方法,但是可以在子線程中刷新
postInvalidate();

?//view的布局參數改變之后刷新,比如view的寬度和高度都修改了,只能通過requestLayout()方法刷新
requestLayout();
?

前言

  • 自定義View原理是Android開發者必須了解的基礎;
  • 在了解自定義View之前,你需要有一定的知識儲備;
  • 本文將全面解析關于自定義View中的所有知識基礎。

目錄


1. View的分類

視圖View主要分為兩類:

類別解釋特點
單一視圖即一個View,如TextView不包含子View
視圖組即多個View組成的ViewGroup,如LinearLayout包含子View

2. View類簡介

  • View類是Android中各種組件的基類,如View是ViewGroup基類
  • View表現為顯示在屏幕上的各種視圖

    Android中的UI組件都由View、ViewGroup組成。

  • View的構造函數:共有4個,具體如下:

    自定義View必須重寫至少一個構造函數:

// 如果View是在Java代碼里面new的,則調用第一個構造函數public CarsonView(Context context) {super(context);}// 如果View是在.xml里聲明的,則調用第二個構造函數 // 自定義屬性是從AttributeSet參數傳進來的public CarsonView(Context context, AttributeSet attrs) {super(context, attrs);}// 不會自動調用 // 一般是在第二個構造函數里主動調用 // 如View有style屬性時public CarsonView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}//API21之后才使用// 不會自動調用// 一般是在第二個構造函數里主動調用// 如View有style屬性時public CarsonView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);}

更加具體的使用請看:深入理解View的構造函數和
理解View的構造函數


3. View視圖結構

對于多View的視圖,結構是樹形結構:最頂層是ViewGroup,ViewGroup下可能有多個ViewGroup或View,如下圖:

請記住:無論是measure過程、layout過程還是draw過程,永遠都是從View樹的根節點開始測量或計算(即從樹的頂端開始),一層一層、一個分支一個分支地進行(即樹形遞歸),最終計算整個View樹中各個View,最終確定整個View樹的相關屬性。


4. Android坐標系

Android的坐標系定義為:

  • 屏幕的左上角為坐標原點
  • 向右為x軸增大方向
  • 向下為y軸增大方向

具體如下圖:

注:區別于一般的數學坐標系


5. View位置(坐標)描述

  • View的位置由4個頂點決定的(如下A、B、C、D)

4個頂點的位置描述分別由4個值決定:
(請記住:View的位置是相對于父控件而言的

  • Top:子View上邊界到父view上邊界的距離
  • Left:子View左邊界到父view左邊界的距離
  • Bottom:子View下邊距到父View上邊界的距離
  • Right:子View右邊界到父view左邊界的距離

如下圖:

個人建議:按頂點位置來記憶:

  • Top:子View左上角距父View頂部的距離;
  • Left:子View左上角距父View左側的距離;
  • Bottom:子View右下角距父View頂部的距離
  • Right:子View右下角距父View左側的距離

6. 位置獲取方式

  • View的位置是通過view.getxxx()函數進行獲取:(以Top為例)
// 獲取Top位置 public final int getTop() { return mTop; } // 其余如下:getLeft(); //獲取子View左上角距父View左側的距離getBottom(); //獲取子View右下角距父View頂部的距離getRight(); //獲取子View右下角距父View左側的距離
  • 與MotionEvent中 get()和getRaw()的區別
//get() :觸摸點相對于其所在組件坐標系的坐標event.getX(); event.getY();//getRaw() :觸摸點相對于屏幕默認坐標系的坐標event.getRawX(); event.getRawY();

具體如下圖:

7. Android的角度(angle)與弧度(radian)

  • 自定義View實際上是將一些簡單的形狀通過計算,從而組合到一起形成的效果。

    這會涉及到畫布的相關操作(旋轉)、正余弦函數計算等,即會涉及到角度(angle)與弧度(radian)的相關知識。

  • 角度和弧度都是描述角的一種度量單位,區別如下圖::

在默認的屏幕坐標系中角度增大方向為順時針。

注:在常見的數學坐標系中角度增大方向為逆時針


8. Android中顏色相關內容

Android中的顏色相關內容包括顏色模式,創建顏色的方式,以及顏色的混合模式等。

8.1 顏色模式

Android支持的顏色模式:

以ARGB8888為例介紹顏色定義:

8.2 定義顏色的方式

8.2.1 在java中定義顏色

//java中使用Color類定義顏色int color = Color.GRAY; //灰色//Color類是使用ARGB值進行表示int color = Color.argb(127, 255, 0, 0); //半透明紅色int color = 0xaaff0000; //帶有透明度的紅色

8.2.2 在xml文件中定義顏色

在/res/values/color.xml 文件中如下定義:

<?xml version="1.0" encoding="utf-8"?> <resources>//定義了紅色(沒有alpha(透明)通道)<color name="red">#ff0000</color>//定義了藍色(沒有alpha(透明)通道)<color name="green">#00ff00</color> </resources>

在xml文件中以”#“開頭定義顏色,后面跟十六進制的值,有如下幾種定義方式:

#f00 //低精度 - 不帶透明通道紅色#af00 //低精度 - 帶透明通道紅色#ff0000 //高精度 - 不帶透明通道紅色#aaff0000 //高精度 - 帶透明通道紅色

8.3 引用顏色的方式

8.3.1 在java文件中引用xml中定義的顏色:

//方法1 int color = getResources().getColor(R.color.mycolor);//方法2(API 23及以上) int color = getColor(R.color.myColor);

8.3.2 在xml文件(layout或style)中引用或者創建顏色

<!--在style文件中引用--><style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"><item name="colorPrimary">@color/red</item></style><!--在layout文件中引用在/res/values/color.xml中定義的顏色-->android:background="@color/red" <!--在layout文件中創建并使用顏色-->android:background="#ff0000"

--------------------- 作者:Carson_Ho 來源:CSDN 原文:https://blog.csdn.net/carson_ho/article/details/56009827?utm_source=copy 版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

?

使用實例:

?

1、Android自定義View構造函數詳解


public DragBallView(Context context) {
? ? this(context, null);
? ? LgqLogutil.e("1");
}
?
public DragBallView(Context context, @Nullable AttributeSet attrs) {
? ? this(context, attrs, 1);
? ? LgqLogutil.e("2");
}
?
public DragBallView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
? ? super(context, attrs, defStyleAttr);
? ? initPaint();
? ? initPoint();
?
}
new引用,調用第一個構造函數

DragBallView dragBallView = new DragBallView(this);
xml文件引用調用第二個構造函數

<com.tianxin.ttttest.DragBallView
? ? android:layout_width="match_parent"
? ? android:layout_height="match_parent"
? ? android:id="@+id/mydrag"/>
第三個可在第二個函數調用之

?

2、單位轉換


/**
?* dp 2 px圖片
?*
?* @param dpVal
?*/
protected int dp2px(int dpVal) {
? ? return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
? ? ? ? ? ? dpVal, getResources().getDisplayMetrics());
}
?
/**
?* sp 2 px文字
?*
?* @param spVal
?* @return
?*/
protected int sp2px(int spVal) {
? ? return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
? ? ? ? ? ? spVal, getResources().getDisplayMetrics());
}


3、判斷觸摸點是否在View范圍之內


//是否可拖拽
private boolean mIsCanDrag = false;
/**
?* 判斷是否可以拖拽
?*
?* @param event event
?*/
private void setIsCanDrag(MotionEvent event) {
? ? Rect rect = new Rect();
? ? rect.left = (int) (startX - radiusStart);
? ? rect.top = (int) (startY - radiusStart);
? ? rect.right = (int) (startX + radiusStart);
? ? rect.bottom = (int) (startY + radiusStart);
?
? ? //觸摸點是否在圓的坐標域內
? ? mIsCanDrag = rect.contains((int) event.getX(), (int) event.getY());
}
@Override
public boolean onTouchEvent(MotionEvent event) {
?
? ? float currentX;
? ? float currentY;
?
? ? switch (event.getAction()) {
?
? ? ? ? case MotionEvent.ACTION_DOWN:
? ? ? ? ? ? setIsCanDrag(event);


4、實時刷新onDraw方法

invalidate();


5、實現構造函數方法以及onSizeChanged方法和onDraw方法即可實現自定義View


onSizeChanged初始化數據

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
? ? super.onSizeChanged(w, h, oldw, oldh);
? ? startX = w / 2;
? ? startY = h / 2;
onDraw方法實現

(1)繪制背景。由private方法drawBackground()完成。如果未設定背景Drawable對象,則會直接返回。?
(2)繪制內容。由onDraw()方法完成。?
(3)繪制子視圖。由dispatchView()完成。View的dispatchView()是空方法,ViewGroup的dispatchView()有具體實現,主要是調用子視圖的draw()方法。?
(4)繪制裝飾。主要是foreground與滾動條。?
因此,只要最高層的根布局調用了它的draw()方法,低層的所有view的draw()方法最終都會被調用。因此,大部分情況下draw()方法是不需要手動調用的,我們只需要覆蓋onDraw()方法,將自己的內容繪制出來即可。
?

畫圓

canvas.drawCircle(pointF.x, pointF.y, radius, circlePaint);

cx:圓心的x坐標。
cy:圓心的y坐標。
radius:圓的半徑。
paint:繪制時所使用的畫筆

畫框:

? ? ? ? mPaint.setStrokeWidth((float) 3.0); ? ? ? ? ? ? ?//線寬
? ? ? ? mPaint.setStyle(Paint.Style.STROKE); ? ? ? ? ? ? ? ? ? //空心效果

畫線

canvas.drawLine(startX, startY, startX, endY, mCenterLinePaint);

虛線

int dp4 = Utils.dp2px(context, 4); dashLine[0] = dp4; dashLine[1] = dp4; void drawLine(Canvas canvas, float startX, float startY, float length) {int index = (int) ((length / (dashLine[0] + dashLine[1]))) + 1;float endY = startY;for (int i = 0; i < index; i++) {endY += dashLine[0];canvas.drawLine(startX, startY, startX, endY, mCenterLinePaint); // endY += dashLine[1];endY += 20;startY = endY;}}

畫矩形

canvas.drawRect(textRect,testpaint);

?橢圓

private RectF mSeatNoRectF = new RectF(); canvas.drawRoundRect(mSeatNoRectF, mSeatNoTopMargin, mSeatNoTopMargin, mSeatNoPaint);

畫字體
canvas.drawText(msgCount > 99 ? "99+" : msgCount + "", textRect.centerX(), ?baseline, textPaint);
畫貝塞爾曲線

private void drawBezier(Canvas canvas) {
? ? path.reset();
? ? path.moveTo(pointA.x, pointA.y);
? ? path.quadTo(pointO.x, pointO.y, pointB.x, pointB.y);
? ? path.lineTo(pointC.x, pointC.y);
? ? path.quadTo(pointO.x, pointO.y, pointD.x, pointD.y);
? ? path.lineTo(pointA.x, pointA.y);
? ? path.close();
?
? ? canvas.drawPath(path, circlePaint);
}
附:

textRect.left = (int) (point.x - radiusStart+15);
textRect.top = (int) (point.y - radiusStart);
textRect.right = (int) (point.x + radiusStart-15);
textRect.bottom = (int) (point.y + radiusStart);
Paint.FontMetricsInt fontMetrics = textPaint.getFontMetricsInt();
int baseline = (textRect.bottom + textRect.top - fontMetrics.bottom - fontMetrics.top) / 2;

畫滿:

mPaint.setStyle(Paint.Style.FILL);

?

自定義屬性

?

1、values文件夾下創建attrs文件夾

<?xml version="1.0" encoding="utf-8"?> <resources><declare-styleable name="CircleView"><attr name="radius" format="integer"/><attr name="CircleX" format="integer"/><attr name="CircleY" format="integer"/><attr name="age"><flag name='child' value="10"/><flag name="young" value="18"/><flag name="old" value="60"/></attr></declare-styleable> </resources>

?

2、?自定義view使用自定義屬性

?

3、XML文件調用自定義屬性

?

?貝塞爾曲線

/*** @author : LGQ* @date : 2020/09/08 15* @desc :*/ public class WaveBezierView extends View implements View.OnClickListener {private Path mPath;private Paint mPaintBezier;private int mWaveLength;private int mScreenHeight;private int mScreenWidth;private int mCenterY;private int mWaveCount;private ValueAnimator mValueAnimator;//波浪流動X軸偏移量private int mOffsetX;//波浪升起Y軸偏移量private int mOffsetY;private int count = 0;private OnItemClickListener mOnItemClickListener;public WaveBezierView(Context context) {super(context);}public WaveBezierView(Context context, AttributeSet attrs) {super(context, attrs);mPaintBezier = new Paint(Paint.ANTI_ALIAS_FLAG);mPaintBezier.setColor(Color.LTGRAY);mPaintBezier.setStrokeWidth(8);mPaintBezier.setStyle(Paint.Style.STROKE); // mPaintBezier.setStyle(Paint.Style.FILL_AND_STROKE);mWaveLength = 800;}public WaveBezierView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);mPath = new Path();setOnClickListener(this);mScreenHeight = h;mScreenWidth = w;mCenterY = h / 2;//設定波浪在屏幕中央處顯示//此處多加1,是為了預先加載屏幕外的一個波浪,持續報廊移動時的連續性mWaveCount = (int) Math.round(mScreenWidth / mWaveLength + 1.5);Log.i("lgq","波谷數==== "+mWaveCount+"....."+(mScreenWidth / mWaveLength + 1.5)+"mScreenWidth=="+mScreenWidth);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);mPath.reset();//Y坐標每次繪制時減去偏移量,即波浪升高mPath.moveTo(-mWaveLength + mOffsetX, mCenterY); // Log.i("lgq","bogu起點==== "+(-mWaveLength + mOffsetX));//每次循環繪制兩個二階貝塞爾曲線形成一個完整波形(含有一個上拱圓,一個下拱圓)for (int i = 0; i < mWaveCount; i++) {//此處的60是指波浪起伏的偏移量,自定義為60mPath.quadTo(-mWaveLength * 3 / 4 + i * mWaveLength + mOffsetX, mCenterY + 60, -mWaveLength / 2 + i * mWaveLength + mOffsetX, mCenterY);mPath.quadTo(-mWaveLength / 4 + i * mWaveLength + mOffsetX, mCenterY - 60, i * mWaveLength + mOffsetX, mCenterY);//第二種寫法:相對位移 // mPath.rQuadTo(mWaveLength / 4, -60, mWaveLength / 2, 0); // mPath.rQuadTo(mWaveLength / 4, +60, mWaveLength / 2, 0);}mPath.lineTo(mScreenWidth, mScreenHeight);mPath.lineTo(0, mScreenHeight);mPath.close();canvas.drawPath(mPath, mPaintBezier);}@Overridepublic void onClick(View view) {mOnItemClickListener.onItemClick(view,3);//設置動畫運動距離mValueAnimator = ValueAnimator.ofInt(0, mWaveLength);mValueAnimator.setDuration(1000);//設置播放數量無限循環mValueAnimator.setRepeatCount(ValueAnimator.INFINITE); // mValueAnimator.setRepeatCount(1);//設置線性運動的插值器mValueAnimator.setInterpolator(new LinearInterpolator());mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator valueAnimator) {//獲取偏移量,繪制波浪曲線的X橫坐標加上此偏移量,產生移動效果mOffsetX = (int) valueAnimator.getAnimatedValue();count++;invalidate();}});mValueAnimator.start();}public interface OnItemClickListener {void onItemClick(View view, int position);}public void setOnItemClickListener(OnItemClickListener onItemClickListener) {this.mOnItemClickListener = onItemClickListener;} }

?

下載資源:https://blog.csdn.net/Samlss/article/details/81189576#PeasLoadingView_loading_viewhttpsgithubcomsamlssPeasLoadingView_53?

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的Android自定义view详解,使用实例,自定义属性,贝塞尔曲线的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。