<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
這幾天有在學習Jetpack中CameraX的內容,在拍攝視訊的時候想著做一個自定義帶有進度條的可長按控制元件,用來顯示拍攝進度,故記錄下來與大家分享!效果如下:
(篇幅過長是因為有程式碼解析過程,可直接到最後檢視完整程式碼)
這個控制元件較為簡易,從效果中可以看出,控制元件模擬了單擊拍照,長按可以錄製視訊的功能,中途鬆手或者時間到都可以停止錄製
思路很簡單,使用簡單的畫筆工具就可以完成這個控制元件
以上就是全部的思路了,程式碼拆解如下:
(一)繼承自View並實現構造方法,程式碼如下:
public class LongClickView extends View { public int DEFAULT_MAX_SECONDS = 15; public int DEFAULT_ANNULUS_WIDTH = 5; public int DEFAULT_ANNULUS_COLOR; public int DEFAULT_RATE = 50; private Paint mSmallCirclePaint; private Paint mMiddenCirclePaint; private Paint mBigCirclePaint; private Paint mAngleCirclePaint; private int mWidthSize; private Timer mTimer;//計時器 private AtomicInteger mCount = new AtomicInteger(0); private MyClickListener mMyClickListener; private boolean mIsFinish = true; private long mStartTime;//點選的時間 private long mEndTime;//點選結束的時間 private int mMaxSeconds; private int mDelayMilliseconds; private int mAnnulusColor; private float mAnnulusWidth; public interface MyClickListener { void longClickFinish();//長按結束 void singleClickFinish();//單擊結束 } public void setMyClickListener(MyClickListener myClickListener) { mMyClickListener = myClickListener; } public LongClickView(Context context) { this(context, null); } public LongClickView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public LongClickView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); getAttrs(context, attrs); initView(); } }
(二)定義並獲取自定義屬性,屬性以及獲取屬性程式碼如下:
attr_long_click_view.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="LongClickView"> <attr name="maxSeconds" format="integer" /> <attr name="annulusWidth" format="integer" /> <attr name="annulusColor" format="color" /> <attr name="delayMilliseconds" format="integer" /> </declare-styleable> </resources>
private void getAttrs(Context context, @Nullable AttributeSet attrs) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.LongClickView); //maxSeconds 最大的秒數 mMaxSeconds = typedArray.getInt(R.styleable.LongClickView_maxSeconds, DEFAULT_MAX_SECONDS); //annulusWidth 圓環的寬度 mAnnulusWidth = typedArray.getInt(R.styleable.LongClickView_annulusWidth, DEFAULT_ANNULUS_WIDTH); //annulusColor 圓環的顏色 DEFAULT_ANNULUS_COLOR = context.getResources().getColor(R.color.color_grey); mAnnulusColor = typedArray.getColor(R.styleable.LongClickView_annulusColor, DEFAULT_ANNULUS_COLOR); //delayMilliseconds 進度條隔多少時間走一次,值越小走的越快,顯得更流暢 mDelayMilliseconds = typedArray.getInt(R.styleable.LongClickView_delayMilliseconds, DEFAULT_RATE); }
(三)定義畫筆工具 的程式碼如下:
private void initView() { mBigCirclePaint = new Paint(); mSmallCirclePaint = new Paint(); mMiddenCirclePaint = new Paint(); mAngleCirclePaint = new Paint(); mBigCirclePaint.setStyle(Paint.Style.FILL); mBigCirclePaint.setColor(Color.LTGRAY); mBigCirclePaint.setAntiAlias(true); mBigCirclePaint.setStrokeWidth(5); mSmallCirclePaint.setStrokeWidth(5); mSmallCirclePaint.setAntiAlias(true); mSmallCirclePaint.setColor(Color.WHITE); mSmallCirclePaint.setStyle(Paint.Style.FILL); mMiddenCirclePaint.setStrokeWidth(5); mMiddenCirclePaint.setAntiAlias(true); mMiddenCirclePaint.setColor(Color.LTGRAY); mMiddenCirclePaint.setStyle(Paint.Style.FILL); mAngleCirclePaint.setStrokeWidth(5); mAngleCirclePaint.setAntiAlias(true); mAngleCirclePaint.setColor(mAnnulusColor); mAngleCirclePaint.setStyle(Paint.Style.FILL); ...//這裡是長按監聽 }
(四)onMeasure中測量大小,onDraw中繪製圓與扇形,程式碼如下:
onMeasure中,如果沒有定義實際寬高就會使用父元件的寬高,如果有實際寬高便會使用自己的寬高
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mWidthSize = MeasureSpec.getSize(widthMeasureSpec); setMeasuredDimension(mWidthSize, mWidthSize); }
onDraw中,一共有三層圓形填充繪製以及一層扇形填充繪製,先繪製最外層的灰色圓形,再根據此時的進度繪製一定角度的扇形,然後覆蓋一層灰色的圓形,最後在覆蓋上一層白色的中心圓,並且在繪製過程以及繪製結束時的中心圓半徑不同。程式碼如下:
@Override protected void onDraw(Canvas canvas) { canvas.drawCircle(mWidthSize / 2, mWidthSize / 2, mWidthSize / 2, mBigCirclePaint);//最外層的填充圓 RectF rectF = new RectF(0, 0, mWidthSize, mWidthSize);//進度扇形 if (mCount.get() > 0) { //求出每一次定時器執行所繪製的扇形度數 float perAngle = 360f / mMaxSeconds / (1000f / mDelayMilliseconds); canvas.drawArc(rectF, 0, perAngle * mCount.get(), true, mAngleCirclePaint); } canvas.drawCircle(mWidthSize / 2, mWidthSize / 2, mWidthSize / 2 - mAnnulusWidth, mMiddenCirclePaint);//中間一層灰色的圓 //最後繪製中心圓 if (mIsFinish) { canvas.drawCircle(mWidthSize / 2, mWidthSize / 2, mWidthSize / 2 - mAnnulusWidth, mSmallCirclePaint); } else { canvas.drawCircle(mWidthSize / 2, mWidthSize / 2, mWidthSize / 8, mSmallCirclePaint); } super.onDraw(canvas); }
(五)監聽長按監聽開始定時器並重新整理畫布,監聽觸控事件進行結束的回撥,定時器使用的是Timer類,當時間超過自定義的最大秒數時就會自動停止,並定時重新整理畫布,程式碼如下:
this.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { mIsFinish = false; mCount.set(0); mTimer = new Timer(); mTimer.schedule(new TimerTask() { @Override public void run() { mCount.addAndGet(1); invalidate(); if (mCount.get() * mDelayMilliseconds >= mMaxSeconds * 1000) { mCount.set(0); this.cancel(); invalidate(); mIsFinish = true; if (mMyClickListener != null) { mMyClickListener.longClickFinish(); } } } }, 0, mDelayMilliseconds); return true; } });
@Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_UP) { mEndTime = System.currentTimeMillis(); new MyAsyncTask().execute(); } else if (event.getAction() == MotionEvent.ACTION_DOWN) { mStartTime = System.currentTimeMillis(); } return super.onTouchEvent(event); }
將定時器停止與停止後的判斷邏輯放在AsyncTask中編寫,確保定時器不會繼續處理邏輯之後再做判斷
public class MyAsyncTask extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... voids) { if (mTimer != null) { mTimer.cancel(); } return null; } @Override protected void onPostExecute(Void aVoid) { //使用時間戳的差來判斷是單擊或者長按 if (mEndTime - mStartTime > 1000) { //防止在自動結束後鬆開手指又重新呼叫了一次長按結束的回撥 if (!mIsFinish) { if (mMyClickListener != null) { mMyClickListener.longClickFinish(); } } } else { //若是單擊就清除進度條 mCount.set(0); invalidate(); if (mMyClickListener != null) { mMyClickListener.singleClickFinish(); } } mIsFinish = true; } }
結束後的回撥類程式碼如下:
public interface MyClickListener { void longClickFinish();//長按結束 void singleClickFinish();//單擊結束 }
public class LongClickView extends View { public int DEFAULT_MAX_SECONDS = 15; public int DEFAULT_ANNULUS_WIDTH = 5; public int DEFAULT_ANNULUS_COLOR; public int DEFAULT_RATE = 50; private Paint mSmallCirclePaint; private Paint mMiddenCirclePaint; private Paint mBigCirclePaint; private Paint mAngleCirclePaint; private int mWidthSize; private Timer mTimer;//計時器 private AtomicInteger mCount = new AtomicInteger(0); private MyClickListener mMyClickListener; private boolean mIsFinish = true; private long mStartTime;//點選的時間 private long mEndTime;//點選結束的時間 private int mMaxSeconds; private int mDelayMilliseconds; private int mAnnulusColor; private float mAnnulusWidth; public interface MyClickListener { void longClickFinish();//長按結束 void singleClickFinish();//單擊結束 } public void setMyClickListener(MyClickListener myClickListener) { mMyClickListener = myClickListener; } public LongClickView(Context context) { this(context, null); } public LongClickView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public LongClickView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); getAttrs(context, attrs); initView(); } private void getAttrs(Context context, @Nullable AttributeSet attrs) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.LongClickView); //maxSeconds 最大的秒數 mMaxSeconds = typedArray.getInt(R.styleable.LongClickView_maxSeconds, DEFAULT_MAX_SECONDS); //annulusWidth 圓環的寬度 mAnnulusWidth = typedArray.getInt(R.styleable.LongClickView_annulusWidth, DEFAULT_ANNULUS_WIDTH); //annulusColor 圓環的顏色 DEFAULT_ANNULUS_COLOR = context.getResources().getColor(R.color.color_grey); mAnnulusColor = typedArray.getColor(R.styleable.LongClickView_annulusColor, DEFAULT_ANNULUS_COLOR); //delayMilliseconds 進度條隔多少時間走一次,值越小走的越快,顯得更流暢 mDelayMilliseconds = typedArray.getInt(R.styleable.LongClickView_delayMilliseconds, DEFAULT_RATE); } private static final String TAG = "LongClickView"; private void initView() { mBigCirclePaint = new Paint(); mSmallCirclePaint = new Paint(); mMiddenCirclePaint = new Paint(); mAngleCirclePaint = new Paint(); mBigCirclePaint.setStyle(Paint.Style.FILL); mBigCirclePaint.setColor(Color.LTGRAY); mBigCirclePaint.setAntiAlias(true); mBigCirclePaint.setStrokeWidth(5); mSmallCirclePaint.setStrokeWidth(5); mSmallCirclePaint.setAntiAlias(true); mSmallCirclePaint.setColor(Color.WHITE); mSmallCirclePaint.setStyle(Paint.Style.FILL); mMiddenCirclePaint.setStrokeWidth(5); mMiddenCirclePaint.setAntiAlias(true); mMiddenCirclePaint.setColor(Color.LTGRAY); mMiddenCirclePaint.setStyle(Paint.Style.FILL); mAngleCirclePaint.setStrokeWidth(5); mAngleCirclePaint.setAntiAlias(true); mAngleCirclePaint.setColor(mAnnulusColor); mAngleCirclePaint.setStyle(Paint.Style.FILL); this.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { mIsFinish = false; mCount.set(0); mTimer = new Timer(); mTimer.schedule(new TimerTask() { @Override public void run() { mCount.addAndGet(1); invalidate(); if (mCount.get() * mDelayMilliseconds >= mMaxSeconds * 1000) { mCount.set(0); this.cancel(); invalidate(); mIsFinish = true; if (mMyClickListener != null) { mMyClickListener.longClickFinish(); } } } }, 0, mDelayMilliseconds); return true; } }); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mWidthSize = MeasureSpec.getSize(widthMeasureSpec); setMeasuredDimension(mWidthSize, mWidthSize); } @Override protected void onDraw(Canvas canvas) { canvas.drawCircle(mWidthSize / 2, mWidthSize / 2, mWidthSize / 2, mBigCirclePaint);//最外層的填充圓 RectF rectF = new RectF(0, 0, mWidthSize, mWidthSize);//進度扇形 if (mCount.get() > 0) { //求出每一次定時器執行所繪製的扇形度數 float perAngle = 360f / mMaxSeconds / (1000f / mDelayMilliseconds); canvas.drawArc(rectF, 0, perAngle * mCount.get(), true, mAngleCirclePaint); } canvas.drawCircle(mWidthSize / 2, mWidthSize / 2, mWidthSize / 2 - mAnnulusWidth, mMiddenCirclePaint);//中間一層灰色的圓 //最後繪製中心圓 if (mIsFinish) { canvas.drawCircle(mWidthSize / 2, mWidthSize / 2, mWidthSize / 2 - mAnnulusWidth, mSmallCirclePaint); } else { canvas.drawCircle(mWidthSize / 2, mWidthSize / 2, mWidthSize / 8, mSmallCirclePaint); } super.onDraw(canvas); } @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_UP) { mEndTime = System.currentTimeMillis(); new MyAsyncTask().execute(); } else if (event.getAction() == MotionEvent.ACTION_DOWN) { mStartTime = System.currentTimeMillis(); } return super.onTouchEvent(event); } public class MyAsyncTask extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... voids) { if (mTimer != null) { mTimer.cancel(); } return null; } @Override protected void onPostExecute(Void aVoid) { //使用時間戳的差來判斷是單擊或者長按 if (mEndTime - mStartTime > 1000) { //防止在結束後鬆開手指有重新呼叫了一次長按結束的回撥 if (!mIsFinish) { if (mMyClickListener != null) { mMyClickListener.longClickFinish(); } } } else { mCount.set(0); invalidate(); if (mMyClickListener != null) { mMyClickListener.singleClickFinish(); } } mIsFinish = true; } } }
使用的程式碼如下:
activity_long_click_view.xml
<?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" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical"> <com.example.customerview.long_click_view.LongClickView android:id="@+id/long_click_view" android:layout_width="100dp" android:layout_height="wrap_content" app:annulusColor="@color/color_2196F3" app:annulusWidth="20" app:delayMilliseconds="40" app:maxSeconds="4" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:text="長按錄製視訊,單擊拍照" android:textColor="@color/colorBlack" android:textSize="20dp" /> </LinearLayout>
LongClickViewActivity.java
mLongClickView.setMyClickListener(new LongClickView.MyClickListener() { @Override public void longClickFinish() { runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(LongClickViewActivity.this, "長按結束", Toast.LENGTH_SHORT).show(); } }); } @Override public void singleClickFinish() { runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(LongClickViewActivity.this, "單擊結束", Toast.LENGTH_SHORT).show(); } }); } });
到此這篇關於Android自定義帶有圓形進度條的可長按控制元件功能的文章就介紹到這了,更多相關Android圓形進度條內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45