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

歡迎訪問 生活随笔!

生活随笔

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

Android

Android BitmapShader 实战 实现圆形、圆角图片

發布時間:2023/12/10 Android 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android BitmapShader 实战 实现圆形、圆角图片 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載請標明出處:http://blog.csdn.net/lmj623565791/article/details/41967509,本文出自:【張鴻洋的博客】

1、概述

記得初學那會寫過一篇博客Android 完美實現圖片圓角和圓形(對實現進行分析),主要是個自定View加上使用Xfermode實現的。其實實現圓角圖片的方法應該很多,常見的就是利用Xfermode,Shader。本篇博客會直接繼承直接繼承ImageView,使用BitmapShader實現圓角的繪制,大家如果耐著性子看完,我估計什么形狀都能繪制出來。

2、效果圖

這是圓角的一個演示圖~~這個沒什么說的,直接設置的圓角的大小就行;

?

?

這是圓形的顯示圖,這里需要注意下,因為設置的圖片可能是長方形,例如上圖:有兩個長方形,一個寬比較大,一個高比較大;

那么我們希望顯示成圓形,我們可能就要對其進行放大或者縮小(因為圖片的寬可能不滿足設置的邊長,而高超出,此時我們就需要放大其寬度)。

?

?

這個一張圖,中間是正常尺寸;上下分別為特大特小,主要可以當尺寸大于或者小于設置尺寸,我們需要對其放大或者縮小;

圓角時如果圖片與view的寬高不一致,也需要進行放大縮小,這里就不截圖了,代碼里面看吧。

3、淺談BitmapShader

BitmapShader是Shader的子類,可以通過Paint.setShader(Shader shader)進行設置、

這里我們只關注BitmapShader,構造方法:

mBitmapShader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP);

參數1:bitmap

參數2,參數3:TileMode;

TileMode的取值有三種:

CLAMP 拉伸

REPEAT 重復

MIRROR 鏡像

如果大家給電腦屏幕設置屏保的時候,如果圖片太小,可以選擇重復、拉伸、鏡像;

重復:就是橫向、縱向不斷重復這個bitmap

鏡像:橫向不斷翻轉重復,縱向不斷翻轉重復;

拉伸:這個和電腦屏保的模式應該有些不同,這個拉伸的是圖片最后的那一個像素;橫向的最后一個橫行像素,不斷的重復,縱項的那一列像素,不斷的重復;

現在大概明白了,BitmapShader通過設置給mPaint,然后用這個mPaint繪圖時,就會根據你設置的TileMode,對繪制區域進行著色。

這里需要注意一點:就是BitmapShader是從你的畫布的左上角開始繪制的,不在view的右下角繪制個正方形,它不會在你正方形的左上角開始。

好了,到此,我相信大家對BitmapShader有了一定的了解了;當然了,如果你希望對Shader充分的了解,請參考愛歌的神作:?自定義控件其實很簡單1/3?。

對于我們的圓角,以及圓形,我們設置的模式都是CLAMP ,但是你會不會會有一個疑問:

view的寬或者高大于我們的bitmap寬或者高豈不是會拉伸?

嗯,我們會為BitmapShader設置一個matrix,去適當的放大或者縮小圖片,不會讓“?view的寬或者高大于我們的bitmap寬或者高 ”此條件成立的。

到此我們的原理基本介紹完畢了,拿到drawable轉化為bitmap,然后直接初始化BitmapShader,畫筆設置Shader,最后在onDraw里面進行畫圓就行了。

4、BitmapShader實戰

首先就來看看利用BitmapShader實現的圓形或者圓角。

我們這里直接繼承ImageView,這樣大家設置圖片的代碼會比較熟悉;但是我們需要支持兩種模式,那么就需要自定義屬性了:

1、自定義屬性

values/attr.xml

[html] view plaincopy
  • <?xml?version="1.0"?encoding="utf-8"?>??
  • <resources>??
  • ??
  • ????<attr?name="borderRadius"?format="dimension"?/>??
  • ????<attr?name="type">??
  • ????????<enum?name="circle"?value="0"?/>??
  • ????????<enum?name="round"?value="1"?/>??
  • ????</attr>??
  • ????
  • ??
  • ????<declare-styleable?name="RoundImageView">??
  • ????????<attr?name="borderRadius"?/>??
  • ????????<attr?name="type"?/>??
  • ????</declare-styleable>??
  • ??
  • </resources>??
  • 我們定義了一個枚舉和一個圓角的大小borderRadius。


    2、獲取自定義屬性

    [java] view plaincopy
  • public?class?RoundImageView?extends?ImageView??
  • {??
  • ??
  • ????/**?
  • ?????*?圖片的類型,圓形or圓角?
  • ?????*/??
  • ????private?int?type;??
  • ????private?static?final?int?TYPE_CIRCLE?=?0;??
  • ????private?static?final?int?TYPE_ROUND?=?1;??
  • ??
  • ????/**?
  • ?????*?圓角大小的默認值?
  • ?????*/??
  • ????private?static?final?int?BODER_RADIUS_DEFAULT?=?10;??
  • ????/**?
  • ?????*?圓角的大小?
  • ?????*/??
  • ????private?int?mBorderRadius;??
  • ??
  • ????/**?
  • ?????*?繪圖的Paint?
  • ?????*/??
  • ????private?Paint?mBitmapPaint;??
  • ????/**?
  • ?????*?圓角的半徑?
  • ?????*/??
  • ????private?int?mRadius;??
  • ????/**?
  • ?????*?3x3?矩陣,主要用于縮小放大?
  • ?????*/??
  • ????private?Matrix?mMatrix;??
  • ????/**?
  • ?????*?渲染圖像,使用圖像為繪制圖形著色?
  • ?????*/??
  • ????private?BitmapShader?mBitmapShader;??
  • ????/**?
  • ?????*?view的寬度?
  • ?????*/??
  • ????private?int?mWidth;??
  • ????private?RectF?mRoundRect;??
  • ??
  • ????public?RoundImageView(Context?context,?AttributeSet?attrs)??
  • ????{??
  • ????????super(context,?attrs);??
  • ????????mMatrix?=?new?Matrix();??
  • ????????mBitmapPaint?=?new?Paint();??
  • ????????mBitmapPaint.setAntiAlias(true);??
  • ??
  • ????????TypedArray?a?=?context.obtainStyledAttributes(attrs,??
  • ????????????????R.styleable.RoundImageView);??
  • ??
  • ????????mBorderRadius?=?a.getDimensionPixelSize(??
  • ????????????????R.styleable.RoundImageView_borderRadius,?(int)?TypedValue??
  • ????????????????????????.applyDimension(TypedValue.COMPLEX_UNIT_DIP,??
  • ????????????????????????????????BODER_RADIUS_DEFAULT,?getResources()??
  • ????????????????????????????????????????.getDisplayMetrics()));//?默認為10dp??
  • ????????type?=?a.getInt(R.styleable.RoundImageView_type,?TYPE_CIRCLE);//?默認為Circle??
  • ??
  • ????????a.recycle();??
  • ????}??
  • ?

    可以看到我們的一些成員變量,基本都加了注釋;然后在構造方法中獲取了我們的自定義屬性,以及部分變量的初始化。

    ?

    3、onMeasure

    [java] view plaincopy
  • @Override??
  • ????protected?void?onMeasure(int?widthMeasureSpec,?int?heightMeasureSpec)??
  • ????{??
  • ????????Log.e("TAG",?"onMeasure");??
  • ????????super.onMeasure(widthMeasureSpec,?heightMeasureSpec);??
  • ??
  • ????????/**?
  • ?????????*?如果類型是圓形,則強制改變view的寬高一致,以小值為準?
  • ?????????*/??
  • ????????if?(type?==?TYPE_CIRCLE)??
  • ????????{??
  • ????????????mWidth?=?Math.min(getMeasuredWidth(),?getMeasuredHeight());??
  • ????????????mRadius?=?mWidth?/?2;??
  • ????????????setMeasuredDimension(mWidth,?mWidth);??
  • ????????}??
  • ??
  • ????}??

  • 我們復寫了onMeasure方法,主要用于當設置類型為圓形時,我們強制讓view的寬和高一致。

    接下來只剩下設置BitmapShader和繪制了

    ?

    4、設置BitmapShader

    [java] view plaincopy
  • /**?
  • ?????*?初始化BitmapShader?
  • ?????*/??
  • ????private?void?setUpShader()??
  • ????{??
  • ????????Drawable?drawable?=?getDrawable();??
  • ????????if?(drawable?==?null)??
  • ????????{??
  • ????????????return;??
  • ????????}??
  • ??
  • ????????Bitmap?bmp?=?drawableToBitamp(drawable);??
  • ????????//?將bmp作為著色器,就是在指定區域內繪制bmp??
  • ????????mBitmapShader?=?new?BitmapShader(bmp,?TileMode.CLAMP,?TileMode.CLAMP);??
  • ????????float?scale?=?1.0f;??
  • ????????if?(type?==?TYPE_CIRCLE)??
  • ????????{??
  • ????????????//?拿到bitmap寬或高的小值??
  • ????????????int?bSize?=?Math.min(bmp.getWidth(),?bmp.getHeight());??
  • ????????????scale?=?mWidth?*?1.0f?/?bSize;??
  • ??
  • ????????}?else?if?(type?==?TYPE_ROUND)??
  • ????????{??
  • ????????????//?如果圖片的寬或者高與view的寬高不匹配,計算出需要縮放的比例;縮放后的圖片的寬高,一定要大于我們view的寬高;所以我們這里取大值;??
  • ????????????scale?=?Math.max(getWidth()?*?1.0f?/?bmp.getWidth(),?getHeight()??
  • ????????????????????*?1.0f?/?bmp.getHeight());??
  • ????????}??
  • ????????//?shader的變換矩陣,我們這里主要用于放大或者縮小??
  • ????????mMatrix.setScale(scale,?scale);??
  • ????????//?設置變換矩陣??
  • ????????mBitmapShader.setLocalMatrix(mMatrix);??
  • ????????//?設置shader??
  • ????????mBitmapPaint.setShader(mBitmapShader);??
  • ????}??
  • 在setUpShader中,首先對drawable轉化為我們的bitmap;

    然后初始化mBitmapShader = new BitmapShader(bmp, TileMode.CLAMP, TileMode.CLAMP);

    接下來,根據類型以及bitmap和view的寬高,計算scale;

    關于scale的計算:

    圓形時:取bitmap的寬或者高的小值作為基準,如果采用大值,縮放后肯定不能填滿我們的圓形區域。然后,view的mWidth/bSize ; 得到的就是scale。

    圓角時:因為設計到寬/高比例,我們分別getWidth() * 1.0f / bmp.getWidth() 和?getHeight()?* 1.0f / bmp.getHeight()?;最終取大值,因為我們要讓最終縮放完成的圖片一定要大于我們的view的區域,有點類似centerCrop;

    比如:view的寬高為10*20;圖片的寬高為5*100 ; 最終我們應該按照寬的比例放大,而不是按照高的比例縮小;因為我們需要讓縮放后的圖片,自定大于我們的view寬高,并保證原圖比例。

    有了scale,就可以設置給我們的matrix;

    然后使用mBitmapShader.setLocalMatrix(mMatrix);

    最后將bitmapShader設置給paint。

    關于drawable轉bitmap的代碼:

    [java] view plaincopy
  • /**?
  • ?????*?drawable轉bitmap?
  • ?????*??
  • ?????*?@param?drawable?
  • ?????*?@return?
  • ?????*/??
  • ????private?Bitmap?drawableToBitamp(Drawable?drawable)??
  • ????{??
  • ????????if?(drawable?instanceof?BitmapDrawable)??
  • ????????{??
  • ????????????BitmapDrawable?bd?=?(BitmapDrawable)?drawable;??
  • ????????????return?bd.getBitmap();??
  • ????????}??
  • ????????int?w?=?drawable.getIntrinsicWidth();??
  • ????????int?h?=?drawable.getIntrinsicHeight();??
  • ????????Bitmap?bitmap?=?Bitmap.createBitmap(w,?h,?Bitmap.Config.ARGB_8888);??
  • ????????Canvas?canvas?=?new?Canvas(bitmap);??
  • ????????drawable.setBounds(0,?0,?w,?h);??
  • ????????drawable.draw(canvas);??
  • ????????return?bitmap;??
  • ????}??
  • 最后我們會在onDraw里面調用setUpShader(),然后進行繪制。

    5、繪制

    到此,就剩下最后一步繪制了,因為我們的范圍,以及縮放都完成了,所以真的只剩下繪制了。

    [java] view plaincopy
  • @Override??
  • ????protected?void?onDraw(Canvas?canvas)??
  • ????{??
  • ????????if?(getDrawable()?==?null)??
  • ????????{??
  • ????????????return;??
  • ????????}??
  • ????????setUpShader();??
  • ??
  • ????????if?(type?==?TYPE_ROUND)??
  • ????????{??
  • ????????????canvas.drawRoundRect(mRoundRect,?mBorderRadius,?mBorderRadius,??
  • ????????????????????mBitmapPaint);??
  • ????????}?else??
  • ????????{??
  • ????????????canvas.drawCircle(mRadius,?mRadius,?mRadius,?mBitmapPaint);??
  • ????????????//?drawSomeThing(canvas);??
  • ????????}??
  • ????}??
  • ??????
  • ????@Override??
  • ????protected?void?onSizeChanged(int?w,?int?h,?int?oldw,?int?oldh)??
  • ????{??
  • ????????super.onSizeChanged(w,?h,?oldw,?oldh);??
  • ????????//?圓角圖片的范圍??
  • ????????if?(type?==?TYPE_ROUND)??
  • ????????????mRoundRect?=?new?RectF(0,?0,?getWidth(),?getHeight());??
  • ????}??

  • 繪制就很簡單了,畫個圓,圓角矩形什么的。圓角矩形的限定范圍mRoundRect在onSizeChanged里面進行了初始化。

    5、狀態的存儲與恢復

    當然了,如果內存不足,而恰好我們的Activity置于后臺,不幸被重啟,或者用戶旋轉屏幕造成Activity重啟,我們的View應該也能盡可能的去保存自己的屬性。 狀態保存什么用處呢?比如,現在一個的圓角大小是10dp,用戶點擊后變成50dp;當用戶旋轉以后,或者長時間置于后臺以后,返回我們的Activity應該還是50dp; 我們簡單的存儲一下,當前的type以及mBorderRadius [java] view plaincopy
  • private?static?final?String?STATE_INSTANCE?=?"state_instance";??
  • ????private?static?final?String?STATE_TYPE?=?"state_type";??
  • ????private?static?final?String?STATE_BORDER_RADIUS?=?"state_border_radius";??
  • ??
  • ????@Override??
  • ????protected?Parcelable?onSaveInstanceState()??
  • ????{??
  • ????????Bundle?bundle?=?new?Bundle();??
  • ????????bundle.putParcelable(STATE_INSTANCE,?super.onSaveInstanceState());??
  • ????????bundle.putInt(STATE_TYPE,?type);??
  • ????????bundle.putInt(STATE_BORDER_RADIUS,?mBorderRadius);??
  • ????????return?bundle;??
  • ????}??
  • ??
  • ????@Override??
  • ????protected?void?onRestoreInstanceState(Parcelable?state)??
  • ????{??
  • ????????if?(state?instanceof?Bundle)??
  • ????????{??
  • ????????????Bundle?bundle?=?(Bundle)?state;??
  • ????????????super.onRestoreInstanceState(((Bundle)?state)??
  • ????????????????????.getParcelable(STATE_INSTANCE));??
  • ????????????this.type?=?bundle.getInt(STATE_TYPE);??
  • ????????????this.mBorderRadius?=?bundle.getInt(STATE_BORDER_RADIUS);??
  • ????????}?else??
  • ????????{??
  • ????????????super.onRestoreInstanceState(state);??
  • ????????}??
  • ??
  • ????}??

  • 代碼比較簡單。我們文章中的demo中,第一個,第四個是可以點擊的,點擊后會發生變化,你可以點擊后,然后旋轉屏幕進行測試。 同時我們也對外公布了兩個方法,用于動態修改圓角大小和type [java] view plaincopy
  • public?void?setBorderRadius(int?borderRadius)??
  • ????{??
  • ????????int?pxVal?=?dp2px(borderRadius);??
  • ????????if?(this.mBorderRadius?!=?pxVal)??
  • ????????{??
  • ????????????this.mBorderRadius?=?pxVal;??
  • ????????????invalidate();??
  • ????????}??
  • ????}??
  • ??
  • ????public?void?setType(int?type)??
  • ????{??
  • ????????if?(this.type?!=?type)??
  • ????????{??
  • ????????????this.type?=?type;??
  • ????????????if?(this.type?!=?TYPE_ROUND?&&?this.type?!=?TYPE_CIRCLE)??
  • ????????????{??
  • ????????????????this.type?=?TYPE_CIRCLE;??
  • ????????????}??
  • ????????????requestLayout();??
  • ????????}??
  • ??
  • ????}??
  • ??
  • ????public?int?dp2px(int?dpVal)??
  • ????{??
  • ????????return?(int)?TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,??
  • ????????????????dpVal,?getResources().getDisplayMetrics());??
  • ????}??
  • 最后貼一下我們的布局文件和MainActivity。

    6、調用

    布局文件: [html] view plaincopy
  • <ScrollView?xmlns:android="http://schemas.android.com/apk/res/android"??
  • ????xmlns:tools="http://schemas.android.com/tools"??
  • ????xmlns:zhy="http://schemas.android.com/apk/res/com.zhy.variousshapeimageview"??
  • ????android:layout_width="match_parent"??
  • ????android:layout_height="wrap_content"?>??
  • ??
  • ????<LinearLayout??
  • ????????android:layout_width="match_parent"??
  • ????????android:layout_height="match_parent"??
  • ????????android:orientation="vertical"?>??
  • ??
  • ????????<com.zhy.view.RoundImageView??
  • ????????????android:id="@+id/id_qiqiu"??
  • ????????????android:layout_width="wrap_content"??
  • ????????????android:layout_height="wrap_content"??
  • ????????????android:layout_margin="10dp"??
  • ????????????android:src="@drawable/qiqiu"?>??
  • ????????</com.zhy.view.RoundImageView>??
  • ??
  • ????????<com.zhy.view.RoundImageView??
  • ????????????android:layout_width="200dp"??
  • ????????????android:layout_height="200dp"??
  • ????????????android:layout_margin="10dp"??
  • ????????????android:src="@drawable/aa"?>??
  • ????????</com.zhy.view.RoundImageView>??
  • ??
  • ????????<com.zhy.view.RoundImageView??
  • ????????????android:layout_width="wrap_content"??
  • ????????????android:layout_height="wrap_content"??
  • ????????????android:layout_margin="10dp"??
  • ????????????android:src="@drawable/icon"?>??
  • ????????</com.zhy.view.RoundImageView>??
  • ??
  • ????????<com.zhy.view.RoundImageView??
  • ????????????android:id="@+id/id_meinv"??
  • ????????????android:layout_width="wrap_content"??
  • ????????????android:layout_height="wrap_content"??
  • ????????????android:layout_margin="10dp"??
  • ????????????android:src="@drawable/aa"??
  • ????????????zhy:borderRadius="20dp"??
  • ????????????zhy:type="round"?>??
  • ????????</com.zhy.view.RoundImageView>??
  • ??
  • ????????<com.zhy.view.RoundImageView??
  • ????????????android:layout_width="wrap_content"??
  • ????????????android:layout_height="wrap_content"??
  • ????????????android:layout_margin="10dp"??
  • ????????????android:src="@drawable/icon"??
  • ????????????zhy:borderRadius="40dp"??
  • ????????????zhy:type="round"?>??
  • ????????</com.zhy.view.RoundImageView>??
  • ??
  • ????????<com.zhy.view.RoundImageView??
  • ????????????android:layout_width="wrap_content"??
  • ????????????android:layout_height="wrap_content"??
  • ????????????android:layout_margin="10dp"??
  • ????????????android:src="@drawable/qiqiu"??
  • ????????????zhy:borderRadius="60dp"??
  • ????????????zhy:type="round"?>??
  • ????????</com.zhy.view.RoundImageView>??
  • ????</LinearLayout>??
  • ??
  • </ScrollView>??
  • 沒撒,ScrollView里面一個線性布局,里面一堆RoundImageView。
    MainActivity [java] view plaincopy
  • package?com.zhy.variousshapeimageview;??
  • ??
  • import?android.app.Activity;??
  • import?android.os.Bundle;??
  • import?android.view.View;??
  • import?android.view.View.OnClickListener;??
  • ??
  • import?com.zhy.view.RoundImageView;??
  • ??
  • public?class?MainActivity?extends?Activity??
  • {??
  • ????private?RoundImageView?mQiQiu;??
  • ????private?RoundImageView?mMeiNv?;???
  • ??
  • ????@Override??
  • ????protected?void?onCreate(Bundle?savedInstanceState)??
  • ????{??
  • ????????super.onCreate(savedInstanceState);??
  • ????????setContentView(R.layout.activity_main);??
  • ??????????
  • ????????mQiQiu?=?(RoundImageView)?findViewById(R.id.id_qiqiu);??
  • ????????mMeiNv?=?(RoundImageView)?findViewById(R.id.id_meinv);??
  • ??????????
  • ????????mQiQiu.setOnClickListener(new?OnClickListener()??
  • ????????{??
  • ????????????@Override??
  • ????????????public?void?onClick(View?v)??
  • ????????????{??
  • ????????????????mQiQiu.setType(RoundImageView.TYPE_ROUND);??
  • ????????????}??
  • ????????});??
  • ??????????
  • ????????mMeiNv.setOnClickListener(new?OnClickListener()??
  • ????????{??
  • ??????????????
  • ????????????@Override??
  • ????????????public?void?onClick(View?v)??
  • ????????????{??
  • ????????????????mMeiNv.setBorderRadius(90);??
  • ????????????}??
  • ????????});??
  • ????}??
  • ??
  • }??
  • 好了,到此本篇博客就結束了。大家可以嘗試繪制個五邊形或者神馬的形狀;或者加個邊框神馬的,相信自己修改應該沒問題~~代碼可能會存在bug和不足之處,歡迎您的指出,共同進步。

    ?

    最后的效果圖:

    ?

    ?

    源碼點擊下載

    轉載于:https://www.cnblogs.com/Free-Thinker/p/6721468.html

    總結

    以上是生活随笔為你收集整理的Android BitmapShader 实战 实现圆形、圆角图片的全部內容,希望文章能夠幫你解決所遇到的問題。

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