国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android自定義View實現旋轉的圓形圖片

2019-10-22 18:16:32
字體:
來源:轉載
供稿:網友

自定義View是android開發的一個重要技能,用android提供的2/3D繪制相關類可以實現非常多炫酷的效果,需要實打實的編程基礎。

但是自定義View又是我的弱項,所以最近都在摸索、練習自定義View。今天我寫了一個圓形圖片,同時不斷勻速旋轉的RotateCircleImageView。實現方法是自己想的,但肯定不是最好的實現方法。

自定義View分四步。

一:自定義屬性;
二:創建自定義View,在構造方法中拿到自定義屬性;
三:重寫onMeasure方法;
四:重寫onDraw方法

先來個效果圖

Android,View,旋轉圖片

先在res/values/下新建attrs.xml
自定義屬性

<declare-styleable name="RotateCircleImageView">     <attr name="image" format="reference" />     <attr name="rotate_sd" format="float" />     <attr name="rotate_fx" format="integer" />     <attr name="isRotate" format="boolean" />     <attr name="circle_back_width" format="dimension" />     <attr name="circle_back_color" format="color" />   </declare-styleable> 

創建RotateCircleImageView

public RotateCircleImageView(Context context) {     this(context, null);   }    public RotateCircleImageView(Context context, AttributeSet attrs) {     this(context, attrs, 0);   }    public RotateCircleImageView(Context context, AttributeSet attrs, int defStyleAttr) {     super(context, attrs, defStyleAttr);     initData();   } 

重寫View的三個構造函數,用一參的調用二參的,用二參的調用三參的。在三參的構造里初始化參數。

 

private Bitmap image; private Bitmap tempImage; private Paint paint; private int bkWidth;//黑色圓邊框的寬度 private int rotate_fx=0;//旋轉方向 0=順時針 1=逆時針 private float rotateSD = 0.8f;//每次旋轉的角度--建議范圍0.1f-1,否則會抖動 private boolean isRotate = false;//控制是否旋轉  private void initData() {     paint = new Paint();     paint.setAntiAlias(true);     paint.setDither(true);     TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs,         R.styleable.RotateCircleImageView, defStyleAttr, 0);//用這個類獲得自定義的屬性         paint.setColor(typedArray.getColor(R.styleable.RotateCircleImageView_circle_back_color,         Color.BLACK));     tempImage = BitmapFactory.decodeResource(getResources(), typedArray.getResourceId(         R.styleable.RotateCircleImageView_image, R.mipmap.ic_launcher));     bkWidth = typedArray.getDimensionPixelSize(R.styleable.             RotateCircleImageView_circle_back_width,         DensityUtils.dp2px(context, 100));//黑色邊框的寬度,DensityUtils是我的一個工具類,將dp轉換成px的       rotateSD = typedArray.getFloat(R.styleable.RotateCircleImageView_rotate_sd, 0.8f);     rotate_fx = typedArray.getInt(R.styleable.RotateCircleImageView_rotate_fx, 0);     isRotate = typedArray.getBoolean(R.styleable.RotateCircleImageView_isRotate, true); } 

重寫測量方法:主要是測量包裹內容的情況下寬度和高度的值

@Override   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {     super.onMeasure(widthMeasureSpec, heightMeasureSpec);     int widthMode = MeasureSpec.getMode(widthMeasureSpec);     int widthSize = MeasureSpec.getSize(widthMeasureSpec);     int heightMode = MeasureSpec.getMode(heightMeasureSpec);     int heightSize = MeasureSpec.getSize(heightMeasureSpec);//分別拿到寬高的大小和測量模式     int mWidth;//最終寬度     int mHeight;//最終高度     int yy_width = widthSize;//預測寬度,先假設它等于指定大小或填充窗體     if (widthMode == MeasureSpec.EXACTLY) {       mWidth = widthSize;//如果是指定大小或填充窗體(以后直接說成指定大小),直接設置最終寬度      } else {       yy_width=tempImage.getWidth();//如果是包裹內容,則預測寬度等于圖片寬度       mWidth = yy_width + getPaddingLeft() + getPaddingRight();//最終寬度等于預測寬度加 左右Padding寬度     }     if (heightMode == MeasureSpec.EXACTLY) {       mHeight = heightSize;//同上     } else {       mHeight = getPaddingTop() + getPaddingBottom() + yy_width;//最終高度等于預測寬度加 上下Padding寬度                    //目的是讓控件的寬高相等,但Padding是可以由用戶自由指定的,所以再加上padding  }     if (tempImage.getHeight() < tempImage.getWidth()) {     //這里用Bitmap類提供的縮放方法把圖片縮放成指定大小,如果圖片高度比寬度小,則強制拉伸       image = Bitmap.createScaledBitmap(tempImage, yy_width - bkWidth,           yy_width - bkWidth, false);     } else {           //這里用Bitmap類提供的縮放方法把圖片縮放成指定大小(寬度等于預測的寬度,高度按比例縮放)     //該方法根據參數的寬高強制縮放圖片,所以這里根據寬度算出縮放后的高度       image = Bitmap.createScaledBitmap(tempImage, yy_width - bkWidth,(int) (tempImage.getHeight() /       (((float) tempImage.getWidth()) / yy_width) - bkWidth), false);     }   setMeasuredDimension(mWidth, mHeight);//設置View的寬高,測量結束   } 

假如寬度是指定大小,我希望高度根據這個大小按比例縮放,那么我需要拿到圖片原始大小,所以需要一個tempImage,為什么寫一個臨時的Bitmap?因為我測試的時候發現   假如我用這個image直接把Bitmap.createScaledBitmap(image,xx,xx,false);的返回值賦給image的話,即使我在這行代碼前去用image.getWidth()和Image.getHeight(),返回的值都已經變成縮放后的大小,而不是原始大小,這讓我感到很奇怪。難道BItmap的getWidth和getHeight是異步的嗎?希望知道的人幫我解答。

最后重寫onDraw方法

@Override   protected void onDraw(Canvas canvas) {     super.onDraw(canvas);      canvas.drawCircle(getWidth() / 2, getWidth() / 2 , getWidth() / 2, paint);//繪制黑色圓     canvas.drawBitmap(getCircleBitmap(image, image.getWidth(), rotateSD),         getWidth() / 2 - image.getWidth() / 2,         getHeight() / 2 - image.getWidth() / 2, paint);//繪制圓形圖片     if (isRotate) {       handler.postDelayed(runnable, 16);//16毫秒后啟動子線程   }   } 

getCircleBitmap方法和子線程的代碼:

 

private Bitmap bitmap;   private boolean isCreateBitmap = false;   private Canvas canvas;   private PorterDuffXfermode pdf;   private Paint bitmapPaint;    private Bitmap getCircleBitmap(Bitmap image, int width, float rotate) {     if (!isCreateBitmap) {//節約資源所以這些代碼只需要執行一次       bitmapPaint = new Paint();       bitmapPaint.setAntiAlias(true);//抗鋸齒       bitmapPaint.setDither(true);//忘了是啥....反正效果好點       bitmap = Bitmap.createBitmap(width, width, Bitmap.Config.ARGB_8888);//創建一個指定寬高的空白bitmap       isCreateBitmap = true;       canvas = new Canvas(bitmap);//用那個空白bitmap創建一個畫布       canvas.drawCircle(width / 2, width / 2, width / 2, bitmapPaint);//在畫布上畫個圓       pdf = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);//創建一個混合模式為保留后者相交的部分     }     bitmapPaint.setXfermode(pdf);//設置混合模式 if (rotate_fx==0) {     canvas.rotate(rotate, width / 2, width / 2);//順時針    } else {//旋轉畫布:意思是下一次繪制的內容會被旋轉這么多個角度      canvas.rotate(-rotate, width / 2, width / 2);//逆時針    }     canvas.drawBitmap(image, 0, 0, bitmapPaint);//繪制圖片,(圖片會被旋轉)     bitmapPaint.setXfermode(null);     return bitmap;//這個bitmap在畫布中被旋轉,畫圓,返回后就是一個圓形的bitmap   }    private Handler handler = new Handler();   private Runnable runnable = new Runnable() {     @Override     public void run() {       invalidate();//刷新界面     }   }; 

在第一次執行onDraw方法的時候得到的是一個旋轉了0.8度的bitmap,然后16毫秒后啟動子線程刷新,再次執行onDraw,得到一個再次旋轉0.8度的bitmap,以此類推,所以不斷旋轉。想要轉的快一點就把每次旋轉的角度調大一點,但是不能太大,否則效果很不好。一卡一卡的。這樣就完成了這個自定義view,非常簡單,但是我卻折騰了好久,主要還是測量的時候不夠細心。實現方法都是自己整出來的,如果有更好的實現方法歡迎告知。

最后再暴露兩個方法給外部

public void startRotate() {//開始旋轉     if (!isRotate) {       this.isRotate = true;       invalidate();     }   }    public void stopRotate() {//暫停旋轉     isRotate = false;   } 

然后可以在布局里試試了:

<?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"   android:fitsSystemWindows="true"   android:orientation="vertical">  <com.as.liji.jishiben.view.RotateCircleImageView     android:id="@+id/rcv"     android:layout_width="match_parent"     android:layout_height="wrap_content"     android:layout_centerInParent="true"     app:circle_back_width="80dp"     app:image="@mipmap/sm"     app:isRotate="false"     app:rotate_fx="0"     app:rotate_sd="0.5" />    <TextView     android:id="@+id/tv"     android:layout_width="wrap_content"     android:layout_height="wrap_content"     android:layout_below="@id/rcv"     android:layout_centerHorizontal="true"     android:ellipsize="marquee"     android:text="正在播放:蜘蛛俠插曲--Hold On" />    <LinearLayout     android:layout_width="match_parent"     android:layout_height="wrap_content"     android:layout_below="@id/tv"     android:orientation="horizontal">      <Button       android:layout_width="0dp"       android:layout_height="wrap_content"       android:layout_weight="1"       android:onClick="startRotate"       android:text="開始" />      <Button       android:layout_width="0dp"       android:layout_height="wrap_content"       android:layout_weight="1"       android:onClick="stopRotate"       android:text="暫停" />   </LinearLayout>  </RelativeLayout> 

在activity中拿到控件,重寫兩個按鈕的點擊事件方法:

private RotateCircleImageView rcv;  ........onCreate(){ ........ rcv = (RotateCircleImageView) findViewById(R.id.rcv); } public void startRotate(View v) {     rcv.startRotate();   }    public void stopRotate(View v) {     rcv.stopRotate();   } 

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 阳曲县| 容城县| 渑池县| 土默特右旗| 乳山市| 彭州市| 高邮市| 昌乐县| 调兵山市| 鹿泉市| 赣榆县| 大姚县| 徐水县| 林州市| 若尔盖县| 尚义县| 乌鲁木齐市| 沾化县| 东至县| 天镇县| 洪雅县| 光泽县| 行唐县| 临湘市| 徐州市| 平南县| 垣曲县| 泰安市| 资溪县| 汉源县| 泾源县| 高阳县| 积石山| 麦盖提县| 建宁县| 泸西县| 西畴县| 廊坊市| 扬州市| 五河县| 孟州市|