.相關(guān)方法詳解
1)Paint(畫筆):
就是畫筆,用于設(shè)置繪制風(fēng)格,如:線寬(筆觸粗細(xì)),顏色,透明度和填充風(fēng)格等 直接使用無參構(gòu)造方法就可以創(chuàng)建Paint實(shí)例: Paint paint = new Paint( );
我們可以通過下述方法來設(shè)置Paint(畫筆)的相關(guān)屬性,另外,關(guān)于這個(gè)屬性有兩種, 圖形繪制相關(guān)與文本繪制相關(guān):
2)Canvas(畫布):
畫筆有了,接著就到畫布(Canvas),總不能憑空作畫是吧~常用方法如下:
首先是構(gòu)造方法,Canvas的構(gòu)造方法有兩種:
Canvas(): 創(chuàng)建一個(gè)空的畫布,可以使用setBitmap()方法來設(shè)置繪制具體的畫布。
Canvas(Bitmap bitmap): 以bitmap對(duì)象創(chuàng)建一個(gè)畫布,將內(nèi)容都繪制在bitmap上,因此bitmap不得為null。
接著是 1.drawXXX()方法族:以一定的坐標(biāo)值在當(dāng)前畫圖區(qū)域畫圖,另外圖層會(huì)疊加, 即后面繪畫的圖層會(huì)覆蓋前面繪畫的圖層。 比如:
2.clipXXX()方法族:在當(dāng)前的畫圖區(qū)域裁剪(clip)出一個(gè)新的畫圖區(qū)域,這個(gè)畫圖區(qū)域就是canvas 對(duì)象的當(dāng)前畫圖區(qū)域了。比如:clipRect(new Rect()),那么該矩形區(qū)域就是canvas的當(dāng)前畫圖區(qū)域
3.save()和restore()方法: save( ):用來保存Canvas的狀態(tài)。save之后,可以調(diào)用Canvas的平移、放縮、旋轉(zhuǎn)、錯(cuò)切、裁剪等操作! restore():用來恢復(fù)Canvas之前保存的狀態(tài)。防止save后對(duì)Canvas執(zhí)行的操作對(duì)后續(xù)的繪制有影響。 save()和restore()要配對(duì)使用(restore可以比save少,但不能多),若restore調(diào)用次數(shù)比save多,會(huì)報(bào)錯(cuò)!
4.translate(float dx, float dy): 平移,將畫布的坐標(biāo)原點(diǎn)向左右方向移動(dòng)x,向上下方向移動(dòng)y.canvas的默認(rèn)位置是在(0,0)
5.scale(float sx, float sy):擴(kuò)大,x為水平方向的放大倍數(shù),y為豎直方向的放大倍數(shù)
6.rotate(float degrees):旋轉(zhuǎn),angle指旋轉(zhuǎn)的角度,順時(shí)針旋轉(zhuǎn)
3)Path(路徑)
簡(jiǎn)單點(diǎn)說就是描點(diǎn),連線~在創(chuàng)建好我們的Path路徑后,可以調(diào)用Canvas的drawPath(path,paint) 將圖形繪制出來~常用方法如下:
更高級(jí)的效果可以使用PathEffect類!
幾個(gè)To:
2.動(dòng)手試試:
屬性那么多,肯定要手把手的擼一下,才能加深我們的映像是吧~ 嘿嘿,畫圖要么在View上畫,要么在SurfaceView上畫,這里我們?cè)赩iew上畫吧, 我們定義一個(gè)View類,然后再onDraw()里完成繪制工作!
/**
* Created by Jay on 2015/10/15 0015.
*/
public class MyView extends View{
private Paint mPaint;
public MyView(Context context) {
super(context);
init();
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
mPaint = new Paint();
mPaint.setAntiAlias(true); //抗鋸齒
mPaint.setColor(getResources().getColor(R.color.puple));//畫筆顏色
mPaint.setStyle(Paint.Style.FILL); //畫筆風(fēng)格
mPaint.setTextSize(36); //繪制文字大小,單位px
mPaint.setStrokeWidth(5); //畫筆粗細(xì)
}
//重寫該方法,在這里繪圖
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
}
然后布局那里設(shè)置下這個(gè)View就好,下述代碼都寫在onDrawable中~
1)設(shè)置畫布顏色:
canvas.drawColor(getResources().getColor(R.color.yellow)); //設(shè)置畫布背景顏色
2)繪制圓形:
canvas.drawCircle(200, 200, 100, mPaint); //畫實(shí)心圓
3)繪制矩形:
canvas.drawRect(0, 0, 200, 100, mPaint); //畫矩形
4)繪制Bitmap:
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher), 0, 0, mPaint);
5)繪制弧形區(qū)域:
canvas.drawArc(new RectF(0, 0, 100, 100),0,90,true,mPaint); //繪制弧形區(qū)域
假如true改為false:
6)繪制圓角矩形
canvas.drawRoundRect(new RectF(10,10,210,110),15,15,mPaint); //畫圓角矩形
7)繪制橢圓
canvas.drawOval(new RectF(0,0,200,300),mPaint); //畫橢圓
8)繪制多邊形:
Path path = new Path();
path.moveTo(10, 10); //移動(dòng)到 坐標(biāo)10,10
path.lineTo(100, 50);
path.lineTo(200,40);
path.lineTo(300, 20);
path.lineTo(200, 10);
path.lineTo(100, 70);
path.lineTo(50, 40);
path.close();
canvas.drawPath(path,mPaint);
9)繪制文字:
canvas.drawText("最喜歡看曹神日狗了~",50,50,mPaint); //繪制文字
你也可以沿著某條Path來繪制這些文字:
Path path = new Path();
path.moveTo(50,50);
path.lineTo(100, 100);
path.lineTo(200, 200);
path.lineTo(300, 300);
path.close();
canvas.drawTextOnPath("最喜歡看曹神日狗了~", path, 50, 50, mPaint); //繪制文字
10)繪制自定義的圖形:
代碼來源于網(wǎng)上:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(canvas.getWidth()/2, 200); //將位置移動(dòng)畫紙的坐標(biāo)點(diǎn):150,150
canvas.drawCircle(0, 0, 100, mPaint); //畫圓圈
//使用path繪制路徑文字
canvas.save();
canvas.translate(-75, -75);
Path path = new Path();
path.addArc(new RectF(0,0,150,150), -180, 180);
Paint citePaint = new Paint(mPaint);
citePaint.setTextSize(14);
citePaint.setStrokeWidth(1);
canvas.drawTextOnPath("繪制表盤~", path, 28, 0, citePaint);
canvas.restore();
Paint tmpPaint = new Paint(mPaint); //小刻度畫筆對(duì)象
tmpPaint.setStrokeWidth(1);
float y=100;
int count = 60; //總刻度數(shù)
for(int i=0 ; i <count ; i++){
if(i%5 == 0){
canvas.drawLine(0f, y, 0, y+12f, mPaint);
canvas.drawText(String.valueOf(i/5+1), -4f, y+25f, tmpPaint);
}else{
canvas.drawLine(0f, y, 0f, y +5f, tmpPaint);
}
canvas.rotate(360/count,0f,0f); //旋轉(zhuǎn)畫紙
}
//繪制指針
tmpPaint.setColor(Color.GRAY);
tmpPaint.setStrokeWidth(4);
canvas.drawCircle(0, 0, 7, tmpPaint);
tmpPaint.setStyle(Paint.Style.FILL);
tmpPaint.setColor(Color.YELLOW);
canvas.drawCircle(0, 0, 5, tmpPaint);
canvas.drawLine(0, 10, 0, -65, mPaint);
}
本節(jié)小結(jié):
本節(jié)我們對(duì)android.graphics接口類下的三個(gè)繪圖API:Canvas(畫布),Paint(畫筆),Path(路徑)進(jìn)行 了學(xué)習(xí),方法有很多,別去死記,用到的時(shí)候查就好,這里我們先有個(gè)大概映像即可,自定義控件那里 我們?cè)賮砺m結(jié)~好的,就說這么多
UI組件化對(duì)項(xiàng)目有正向收益,不僅能提效,還能保證高度的視覺還原度,減少和UI設(shè)計(jì)師溝通成本,所以也得到了大家的認(rèn)可。
所以每個(gè)項(xiàng)目都會(huì)啟動(dòng)UI組件化建設(shè),但是UI視圖是和項(xiàng)目強(qiáng)相關(guān)的,項(xiàng)目間無法復(fù)用,導(dǎo)致大家疲于實(shí)現(xiàn),重復(fù)造輪子,拖延下班時(shí)間,那么基于上面的背景,有沒有更好的解決方案呢,答案是有的,下面介紹一下UI組件化在項(xiàng)目中的實(shí)施經(jīng)驗(yàn),下面分為目標(biāo)、工程架構(gòu)、組件架構(gòu)、組件實(shí)現(xiàn)來展開。
對(duì)現(xiàn)有UI組件化進(jìn)行容器化抽象,底層UI組件提供最大功能集合,完全解耦業(yè)務(wù)邏輯,業(yè)務(wù)方根據(jù)自己需求,基于基礎(chǔ)組件開發(fā),通過屬性配置或者組合的方式達(dá)到復(fù)雜的效果,所以只要底層組件抽象的足夠好、能力足夠全,就能大大的提高開發(fā)效率,后期適配也不會(huì)涉及核心邏輯修改,一定程度的保證了功能的穩(wěn)定性
所有的ui組件統(tǒng)一收斂到uikit下,其下面moudle劃分以是否非常通用為依據(jù),如果非業(yè)務(wù)屬性,并且特別通用的模塊組件,抽取單獨(dú)module,方便解耦和復(fù)用,如果不是則統(tǒng)一放在同一個(gè)module下,這樣uikit模塊劃分如下:
空殼工程,可以單獨(dú)運(yùn)行
對(duì)所有的組件提供demo,里面的功能也可以在調(diào)試面板中打開
依賴widget和module,業(yè)務(wù)使用ui組件直接依賴uikit即可
工程架構(gòu)可以分為5層,分別是:基礎(chǔ)控件、組合控件、業(yè)務(wù)UI組件、橋接、demo。
架構(gòu)分層如下圖:
一個(gè)好的架構(gòu)應(yīng)該層次分明、低耦合、高擴(kuò)展,對(duì)組件的增刪支持的足夠友好,任何組件都能準(zhǔn)確的找到對(duì)應(yīng)的分層,并且不會(huì)改動(dòng)到已有代碼,所以review一下剛剛設(shè)計(jì)的架構(gòu),基本上滿足需求,架構(gòu)設(shè)計(jì)符合預(yù)期。
架構(gòu)設(shè)計(jì)好之后的步驟就是實(shí)施了,如何和現(xiàn)有的工程做結(jié)合呢,UI組件按階段可以分為:開發(fā)階段、穩(wěn)定階段,理想的開發(fā)模式為開發(fā)階段在宿主工程中開發(fā)調(diào)試,但是放宿主工程中會(huì)帶了編譯慢的問題,組件開發(fā)和業(yè)務(wù)是接耦的,所以希望代碼在宿主工程,demo和組件開發(fā)可以單獨(dú)運(yùn)行,當(dāng)組件開發(fā)完成,到了穩(wěn)定階段,組件代碼修改頻率降低,同時(shí)加快編譯速度,UIKit組件發(fā)布到遠(yuǎn)程maven倉(cāng)庫(kù),最終uikit工程獨(dú)立出來,單獨(dú)迭代,下面是工程架構(gòu)實(shí)現(xiàn)
UI組件和宿主打包編譯
settings.gradle
includeIfAbsent ':uikit:uikit'
includeIfAbsent ':uikit:demo'
includeIfAbsent ':uikit:imgselector'
includeIfAbsent ':uikit:roundview'
includeIfAbsent ':uikit:widget'
includeIfAbsent ':uikit:photodraweeview'
includeIfAbsent ':uikit:flatbutton'
includeIfAbsent ':uikit:dialog'
includeIfAbsent ':uikit:widgetlayout'
includeIfAbsent ':uikit:statusbar'
includeIfAbsent ':uikit:toolbar'
復(fù)制代碼
common_business.gradle中一鍵依賴
apply from: rootProject.file("library_base.gradle")
dependencies {
...
implementation project(":uikit:uikit")
}
復(fù)制代碼
UI組件獨(dú)立編譯
uikit/shell/settings.gradle
include ':app'
includeModule('widget','../')
includeModule('demo','../')
includeModule('flatbutton','../')
includeModule('imgselector','../')
includeModule('photodraweeview','../')
includeModule('roundview','../')
includeModule('uikit','../')
includeModule('widgetlayout','../')
includeModule('dialog','../')
includeModule('statusbar','../')
includeModule('toolbar','../')
def includeModule(name, filePath = name) {
def projectDir = new File(filePath+name)
if (projectDir.exists()) {
include ':uikit:' + name
project(':uikit:' + name).projectDir = projectDir
} else {
print("settings:could not find module $name in path $filePath")
}
}
復(fù)制代碼
UI組件lib的build.gradle中
if (rootProject.ext.is_in_uikit_project) {
apply from: rootProject.file('../uikit.gradle')
} else {
apply from: rootProject.file('uikit/uikit.gradle')
}
復(fù)制代碼
這樣就實(shí)現(xiàn)了宿主工程UIKit代碼單獨(dú)運(yùn)行的效果了
組件可以分為2類:工具型、業(yè)務(wù)類型,2個(gè)類型的組件迭代思路差異非常的大,工具型組件,只要單點(diǎn)做到極致就ok了,整體比較簡(jiǎn)單,復(fù)用性也比較強(qiáng),而業(yè)務(wù)型組件就會(huì)稍顯復(fù)雜,既要考慮復(fù)用性,也要考慮可擴(kuò)展性,下面分別介紹這2個(gè)類型組件的實(shí)現(xiàn)思路
工具型組件迭代的思路就是不斷的完善基礎(chǔ)能力,盡可能的功能全面,在已有的能力上不斷的支持新的功能,比較重要的就是兼容已有api,比較代表性的組件有FlatButton、RoundView、StatusBar,可以參考下FlatButton&RoundView迭代歷程:
如何做好一個(gè)業(yè)務(wù)組件呢,實(shí)現(xiàn)可以是具象的,也可以是抽象的,好的組件設(shè)計(jì)應(yīng)該是2者兼?zhèn)?,最底層的?shí)現(xiàn)應(yīng)該是足夠抽象,而上層實(shí)現(xiàn)又應(yīng)該是具象的,所以需要帶著容器化的思路來實(shí)現(xiàn),那么怎么個(gè)思路呢,如下圖:
下面以FlatButton為例介紹組件實(shí)現(xiàn)方式,其它組件實(shí)現(xiàn)思路類似。在實(shí)現(xiàn)前,我們先看下視覺稿
按鈕樣式特別多,實(shí)現(xiàn)方式也可以有很多種,現(xiàn)有工程也給出了實(shí)現(xiàn)方案,具體如下:
第一步:分別定義noraml下的shape和pressed的shape,如果enable = false,還得再定義一個(gè)dissable的shape
normal (ui_standard_bg_btn_corner_28_ripple)
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/button_pressed_cover">
<item
android:drawable="@drawable/ui_standard_bg_btn_corner_28_enable">
</item>
</ripple>
復(fù)制代碼
pressed(ui_standard_bg_btn_corner_28_disable)
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:angle="0"
android:endColor="@color/button_disable_end"
android:startColor="@color/button_disable_start"
android:useLevel="false"
android:type="linear" />
<corners android:radius="28dp" />
</shape>
復(fù)制代碼
第二步:定義selector
selector(ui_standard_bg_btn_corner_28)
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="true" android:drawable="@drawable/ui_standard_bg_btn_corner_28_ripple" />
<item android:state_enabled="false" android:drawable="@drawable/ui_standard_bg_btn_corner_28_disable" />
</selector>
復(fù)制代碼
第三步:使用
<TextView
...
android:background="@drawable/ui_standard_bg_btn_corner_28"
android:textColor="@color/white"/>
復(fù)制代碼
這樣按鈕的背景按壓就實(shí)現(xiàn)了,如果在此基礎(chǔ)上,文字也需要按壓態(tài),那么就重復(fù)上面的步驟,對(duì)顏色再創(chuàng)建一個(gè)選擇器,當(dāng)實(shí)現(xiàn)完上面UI定義的樣式后,工程中的畫風(fēng)如下:
我是誰,我在哪里,這該怎么玩,長(zhǎng)得都差不多,基本沒有開發(fā)體驗(yàn),復(fù)用性、擴(kuò)展性都非常的差,如果來個(gè)UI大改版,又得從頭再來一次。那怎么解決上面的問題呢,答案是定義按鈕通用能力,業(yè)務(wù)上層再實(shí)現(xiàn),按這個(gè)思路做,需要?jiǎng)h除上面所有shape、selector,然后自定義控件,我們都知道,上面定義的shape、selector xml文件,android系統(tǒng)最終都是會(huì)解析生成對(duì)應(yīng)的對(duì)象,所以我們借鑒一下系統(tǒng)代碼,實(shí)現(xiàn)起來就so easy
看下這個(gè)shape xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:angle="0"
android:endColor="@color/button_disable_end"
android:startColor="@color/button_disable_start"
android:useLevel="false"
android:type="linear" />
<corners android:radius="28dp" />
</shape>
復(fù)制代碼
解析后的對(duì)象為GradientDrawable
public void setOrientation(Orientation orientation)
public void setColors(@Nullable @ColorInt int[] colors)
public void setCornerRadii(@Nullable float[] radii)
public void setStroke(int width, @ColorInt int color)
...
復(fù)制代碼
也就是說,xml中定義的屬性,代碼中都可以實(shí)現(xiàn),除了GradientDrawable,還會(huì)用到RippleDrawable實(shí)現(xiàn)水波紋,同理文字顏色選擇器代碼中對(duì)應(yīng)的為ColorStateList,有了上面鋪墊,具體實(shí)現(xiàn)如下:
<declare-styleable name="FlatButton">
<!--默認(rèn)背景顏色 -->
<attr name="fb_colorNormal" format="color" />
<!--按下背景顏色 -->
<attr name="fb_colorPressed" format="color" />
<!--Disable背景顏色 -->
<attr name="fb_colorDisable" format="color" />
<!--默認(rèn)開始漸變顏色 -->
<attr name="fb_colorNormalStart" format="color" />
<!--默認(rèn)結(jié)束漸變顏色 -->
<attr name="fb_colorNormalEnd" format="color" />
<!--按下開始漸變顏色 -->
<attr name="fb_colorPressedStart" format="color" />
<!--按下結(jié)束漸變顏色 -->
<attr name="fb_colorPressedEnd" format="color" />
<!--Disable開始漸變顏色 -->
<attr name="fb_colorDisableStart" format="color" />
<!--Disable結(jié)束漸變顏色 -->
<attr name="fb_colorDisableEnd" format="color" />
<!--漸變方向 -->
<attr name="fb_gradientOrientation">
<enum name="left_right" value="0" />
<enum name="right_left" value="1" />
<enum name="top_bottom" value="2" />
<enum name="bottom_top" value="3" />
<enum name="tr_bl" value="4" />
<enum name="bl_tr" value="5" />
<enum name="br_tl" value="6" />
<enum name="tl_br" value="7" />
</attr>
<!--默認(rèn)文字顏色 -->
<attr name="fb_colorNormalText" format="color" />
<!--按下文字顏色 -->
<attr name="fb_colorPressedText" format="color" />
<!--Disable文字顏色 -->
<attr name="fb_colorDisableText" format="color" />
<!--邊框顏色 -->
<attr name="fb_strokeColor" format="color" />
<!--按下邊框顏色 -->
<attr name="fb_strokePressColor" format="color" />
<!--Disable邊框顏色 -->
<attr name="fb_strokeDisableColor" format="color" />
<!--邊框?qū)挾?-->
<attr name="fb_strokeWidth" format="dimension" />
<!--水波紋是否可用 -->
<attr name="fb_isRippleEnable" format="boolean" />
<!--默認(rèn)水波紋顏色 -->
<attr name="fb_colorRippleNormal" format="color" />
<!--按下水波紋顏色 -->
<attr name="fb_colorRipplePressed" format="color" />
<!--圓角角度 -->
<attr name="fb_cornerRadius" format="dimension" />
<!--左上圓角角度 -->
<attr name="fb_radius_TL" format="dimension" />
<!--右上圓角角度 -->
<attr name="fb_radius_TR" format="dimension" />
<!--左下圓角角度 -->
<attr name="fb_radius_BL" format="dimension" />
<!--右下圓角角度 -->
<attr name="fb_radius_BR" format="dimension" />
<!--是否開啟防抖 -->
<attr name="fb_antiShakeEnable" format="boolean" />
</declare-styleable>
復(fù)制代碼
private fun setBackgroundCompat() {
val stateListDrawable = createStateListDrawable()
val pL = paddingLeft
val pT = paddingTop
val pR = paddingRight
val pB = paddingBottom
background = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && isRippleEnable) {
val rippleDrawable = RippleDrawable(createRippleColorStateList(), stateListDrawable, null)
rippleDrawable
} else {
stateListDrawable
}
setPadding(pL, pT, pR, pB)
}
private fun createStateListDrawable(): StateListDrawable {
var normalDrawable = StateListDrawable()
normalDrawable.addState(
intArrayOf(android.R.attr.state_pressed),
createPressedDrawable()
)
normalDrawable.addState(
intArrayOf(android.R.attr.state_focused),
createPressedDrawable()
)
normalDrawable.addState(
intArrayOf(-android.R.attr.state_enabled),
createDisableDrawable()
)
normalDrawable.addState(
intArrayOf(android.R.attr.state_selected),
createPressedDrawable()
)
normalDrawable.addState(intArrayOf(), createNormalDrawable())
return normalDrawable
}
private fun createRippleColorStateList(): ColorStateList {
val stateList = arrayOf(intArrayOf(android.R.attr.state_pressed), intArrayOf(android.R.attr.state_focused), intArrayOf(android.R.attr.state_activated), intArrayOf())
val normalColor = backgroundStyle.getColorRippleNormalFallback()
val pressedColor = backgroundStyle.getColorRipplePressedFallback()
val stateColorList = intArrayOf(
pressedColor,
pressedColor,
pressedColor,
normalColor
)
return ColorStateList(stateList, stateColorList)
}
復(fù)制代碼
xml中使用
<com.snapsolve.uikit.flatbutton.FlatButton
app:fb_colorNormalText="@color/uikit_color_white"
app:fb_colorPressedText="@color/uikit_color_white"
app:fb_colorNormalEnd="#FF9800"
app:fb_colorNormalStart="#FF0000"
app:fb_colorPressedEnd="#4CAF50"
app:fb_colorPressedStart="#009688"
app:fb_colorRippleNormal="#303F9F"
app:fb_colorRipplePressed="#FF4081"
app:fb_cornerRadius="24dp"
app:fb_gradientOrientation="left_right"
app:fb_isRippleEnable="true"
...
/>
復(fù)制代碼
代碼中使用
fb_radius_in_code.setBackgroundStyle {
this.colorNormal = resources.getColor(R.color.uikit_color_FF4081)
this.colorPressed = resources.getColor(R.color.uikit_color_9C27B0)
this.colorRippleNormal = resources.getColor(R.color.uikit_color_FF4081)
this.colorRipplePressed = resources.getColor(R.color.uikit_color_9C27B0)
}.setRadiusStyle {
this.radiusTL = dp2px(24F)
this.radius_BR = dp2px(24F)
}
復(fù)制代碼
到這里,底層Button能力定義完成,接下來就是組件化實(shí)現(xiàn)了,具體實(shí)現(xiàn)方式如下:
無法復(fù)制加載中的內(nèi)容
項(xiàng)目中的按鈕UI按照UI組件要求,可以基于FlatButton來實(shí)現(xiàn),配置好給種類型的屬性,按鈕名字可以和設(shè)計(jì)對(duì)齊,到這里就基本完成了
一級(jí)按鈕、二級(jí)按鈕、三級(jí)按鈕的實(shí)現(xiàn)可以通過繼承FlatButton,設(shè)置默認(rèn)樣式,使用的時(shí)候就不需要再在xml中定義任何屬性,只需記住組件名字,依賴即可,做到真正的開箱即用
舉一個(gè)例子,定義一個(gè)線框button
class StrokeButton : FlatButton {
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
config(context, attrs)
}
private fun config(context: Context, attrs: AttributeSet?){
.setBackgroundStyle {
this.colorNormal = resources.getColor(R.color.uikit_color_FF4081)
this.colorPressed = resources.getColor(R.color.uikit_color_9C27B0)
this.colorRippleNormal = resources.getColor(R.color.uikit_color_FF4081)
this.colorRipplePressed = resources.getColor(R.color.uikit_color_9C27B0)
}.setRadiusStyle {
this.radiusTL = dp2px(28F)
this.radius_BR = dp2px(28F)
}
}
private fun dp2px(dp: Float): Float {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.displayMetrics)
}
}
復(fù)制代碼
業(yè)務(wù)使用
.相關(guān)方法詳解
1)Paint(畫筆):
就是畫筆,用于設(shè)置繪制風(fēng)格,如:線寬(筆觸粗細(xì)),顏色,透明度和填充風(fēng)格等 直接使用無參構(gòu)造方法就可以創(chuàng)建Paint實(shí)例: Paint paint = new Paint( );
我們可以通過下述方法來設(shè)置Paint(畫筆)的相關(guān)屬性,另外,關(guān)于這個(gè)屬性有兩種, 圖形繪制相關(guān)與文本繪制相關(guān):
2)Canvas(畫布):
畫筆有了,接著就到畫布(Canvas),總不能憑空作畫是吧~常用方法如下:
首先是構(gòu)造方法,Canvas的構(gòu)造方法有兩種:
Canvas(): 創(chuàng)建一個(gè)空的畫布,可以使用setBitmap()方法來設(shè)置繪制具體的畫布。
Canvas(Bitmap bitmap): 以bitmap對(duì)象創(chuàng)建一個(gè)畫布,將內(nèi)容都繪制在bitmap上,因此bitmap不得為null。
接著是 1.drawXXX()方法族:以一定的坐標(biāo)值在當(dāng)前畫圖區(qū)域畫圖,另外圖層會(huì)疊加, 即后面繪畫的圖層會(huì)覆蓋前面繪畫的圖層。 比如:
2.clipXXX()方法族:在當(dāng)前的畫圖區(qū)域裁剪(clip)出一個(gè)新的畫圖區(qū)域,這個(gè)畫圖區(qū)域就是canvas 對(duì)象的當(dāng)前畫圖區(qū)域了。比如:clipRect(new Rect()),那么該矩形區(qū)域就是canvas的當(dāng)前畫圖區(qū)域
3.save()和restore()方法: save( ):用來保存Canvas的狀態(tài)。save之后,可以調(diào)用Canvas的平移、放縮、旋轉(zhuǎn)、錯(cuò)切、裁剪等操作! restore():用來恢復(fù)Canvas之前保存的狀態(tài)。防止save后對(duì)Canvas執(zhí)行的操作對(duì)后續(xù)的繪制有影響。 save()和restore()要配對(duì)使用(restore可以比save少,但不能多),若restore調(diào)用次數(shù)比save多,會(huì)報(bào)錯(cuò)!
4.translate(float dx, float dy): 平移,將畫布的坐標(biāo)原點(diǎn)向左右方向移動(dòng)x,向上下方向移動(dòng)y.canvas的默認(rèn)位置是在(0,0)
5.scale(float sx, float sy):擴(kuò)大,x為水平方向的放大倍數(shù),y為豎直方向的放大倍數(shù)
6.rotate(float degrees):旋轉(zhuǎn),angle指旋轉(zhuǎn)的角度,順時(shí)針旋轉(zhuǎn)
3)Path(路徑)
簡(jiǎn)單點(diǎn)說就是描點(diǎn),連線~在創(chuàng)建好我們的Path路徑后,可以調(diào)用Canvas的drawPath(path,paint) 將圖形繪制出來~常用方法如下:
更高級(jí)的效果可以使用PathEffect類!
幾個(gè)To:
2.動(dòng)手試試:
屬性那么多,肯定要手把手的擼一下,才能加深我們的映像是吧~ 嘿嘿,畫圖要么在View上畫,要么在SurfaceView上畫,這里我們?cè)赩iew上畫吧, 我們定義一個(gè)View類,然后再onDraw()里完成繪制工作!
/**
* Created by Jay on 2015/10/15 0015.
*/
public class MyView extends View{
private Paint mPaint;
public MyView(Context context) {
super(context);
init();
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
mPaint = new Paint();
mPaint.setAntiAlias(true); //抗鋸齒
mPaint.setColor(getResources().getColor(R.color.puple));//畫筆顏色
mPaint.setStyle(Paint.Style.FILL); //畫筆風(fēng)格
mPaint.setTextSize(36); //繪制文字大小,單位px
mPaint.setStrokeWidth(5); //畫筆粗細(xì)
}
//重寫該方法,在這里繪圖
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
}
然后布局那里設(shè)置下這個(gè)View就好,下述代碼都寫在onDrawable中~
1)設(shè)置畫布顏色:
canvas.drawColor(getResources().getColor(R.color.yellow)); //設(shè)置畫布背景顏色
2)繪制圓形:
canvas.drawCircle(200, 200, 100, mPaint); //畫實(shí)心圓
3)繪制矩形:
canvas.drawRect(0, 0, 200, 100, mPaint); //畫矩形
4)繪制Bitmap:
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher), 0, 0, mPaint);
5)繪制弧形區(qū)域:
canvas.drawArc(new RectF(0, 0, 100, 100),0,90,true,mPaint); //繪制弧形區(qū)域
假如true改為false:
6)繪制圓角矩形
canvas.drawRoundRect(new RectF(10,10,210,110),15,15,mPaint); //畫圓角矩形
7)繪制橢圓
canvas.drawOval(new RectF(0,0,200,300),mPaint); //畫橢圓
8)繪制多邊形:
Path path = new Path();
path.moveTo(10, 10); //移動(dòng)到 坐標(biāo)10,10
path.lineTo(100, 50);
path.lineTo(200,40);
path.lineTo(300, 20);
path.lineTo(200, 10);
path.lineTo(100, 70);
path.lineTo(50, 40);
path.close();
canvas.drawPath(path,mPaint);
9)繪制文字:
canvas.drawText("最喜歡看曹神日狗了~",50,50,mPaint); //繪制文字
你也可以沿著某條Path來繪制這些文字:
Path path = new Path();
path.moveTo(50,50);
path.lineTo(100, 100);
path.lineTo(200, 200);
path.lineTo(300, 300);
path.close();
canvas.drawTextOnPath("最喜歡看曹神日狗了~", path, 50, 50, mPaint); //繪制文字
10)繪制自定義的圖形:
代碼來源于網(wǎng)上:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(canvas.getWidth()/2, 200); //將位置移動(dòng)畫紙的坐標(biāo)點(diǎn):150,150
canvas.drawCircle(0, 0, 100, mPaint); //畫圓圈
//使用path繪制路徑文字
canvas.save();
canvas.translate(-75, -75);
Path path = new Path();
path.addArc(new RectF(0,0,150,150), -180, 180);
Paint citePaint = new Paint(mPaint);
citePaint.setTextSize(14);
citePaint.setStrokeWidth(1);
canvas.drawTextOnPath("繪制表盤~", path, 28, 0, citePaint);
canvas.restore();
Paint tmpPaint = new Paint(mPaint); //小刻度畫筆對(duì)象
tmpPaint.setStrokeWidth(1);
float y=100;
int count = 60; //總刻度數(shù)
for(int i=0 ; i <count ; i++){
if(i%5 == 0){
canvas.drawLine(0f, y, 0, y+12f, mPaint);
canvas.drawText(String.valueOf(i/5+1), -4f, y+25f, tmpPaint);
}else{
canvas.drawLine(0f, y, 0f, y +5f, tmpPaint);
}
canvas.rotate(360/count,0f,0f); //旋轉(zhuǎn)畫紙
}
//繪制指針
tmpPaint.setColor(Color.GRAY);
tmpPaint.setStrokeWidth(4);
canvas.drawCircle(0, 0, 7, tmpPaint);
tmpPaint.setStyle(Paint.Style.FILL);
tmpPaint.setColor(Color.YELLOW);
canvas.drawCircle(0, 0, 5, tmpPaint);
canvas.drawLine(0, 10, 0, -65, mPaint);
}
本節(jié)小結(jié):
本節(jié)我們對(duì)android.graphics接口類下的三個(gè)繪圖API:Canvas(畫布),Paint(畫筆),Path(路徑)進(jìn)行 了學(xué)習(xí),方法有很多,別去死記,用到的時(shí)候查就好,這里我們先有個(gè)大概映像即可,自定義控件那里 我們?cè)賮砺m結(jié)~好的,就說這么多
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。