實(shí)踐自定義UI—RLF...(RelativeLayout LinearLayout FrameLayout....)

上一篇文章我們利用View進(jìn)行自定義UI护糖,這篇我們將利用Android現(xiàn)有的UI進(jìn)行自定義UI晶疼。我們利用現(xiàn)有的UI控件,主要是利用它們的一些屬性吧恃,并且根據(jù)這些屬性的改變可以達(dá)到我們預(yù)期的效果虾啦。還是看看今天我們實(shí)現(xiàn)的效果吧,No picture痕寓,it's so hard傲醉。效果圖如下所示,就是我們常見的Tab和SeekBar呻率,看看今天怎么用現(xiàn)有的UI控件實(shí)現(xiàn)它硬毕;老樣子我們還是來一步一步分析吧。

效果圖

分解效果圖

我們看到Horizontal和Vertical兩個(gè)Tab是不可以滑動(dòng)的礼仗,只有通過點(diǎn)擊來觸發(fā)左右切換吐咳,在Tab的下面會(huì)有一個(gè)帶顏色的bar標(biāo)志當(dāng)前選中的位置。想這樣的UI目前現(xiàn)有的Android控件應(yīng)該沒有可以直接使用的(據(jù)我的了解)元践。一般我們?cè)趯?shí)現(xiàn)這樣的UI時(shí)挪丢,我們會(huì)用TextView和一個(gè)帶顏色的View組合實(shí)現(xiàn)慎恒。這樣好像也可以趋距,但是還是有一定的代碼量的摘盆。那么我們?cè)趺从米钌俚拇a實(shí)現(xiàn)這樣的需求呢狼渊!首先我們看到左右切換的選中米苹,類似于RadioButton這樣的控件——在多個(gè)選項(xiàng)中只能選取其中一個(gè)亏较,但是RadioButton這樣的控件底部好像也沒有這樣的帶顏色的bar啊巡通,難道還是要利用一個(gè)View和它組合使用嗎弥锄,那這樣還是太low了戒悠。我們想啊绸狐,既然底部沒有這樣一個(gè)bar卤恳,那我們可以讓RadioButton在底部畫一個(gè)啊。怎么畫寒矿?那當(dāng)然是繼承它突琳,在onDraw()方法里面畫啦。具體怎么畫劫窒,我相信畫一個(gè)矩形應(yīng)該很簡單吧_本今。

上面分析完了Tab的部分拆座,這下我們來看看這個(gè)Seekbar吧主巍,我們先看看下面分解的4張圖片。

第一條進(jìn)度條
第二條進(jìn)度條
游標(biāo)
以上三個(gè)疊加效果

從上面的四張圖片可以看出挪凑,這個(gè)Seekbar是通過前面三張一個(gè)一個(gè)疊加過后達(dá)到第四張圖片的效果孕索。當(dāng)我們拖動(dòng)小thumb(小圓球)滑動(dòng)的時(shí)候,不斷的改變第二層上圓角矩形的寬度就可以達(dá)到想要的效果了躏碳。那么具體怎么實(shí)現(xiàn)搞旭,當(dāng)然還是上代碼啦。

實(shí)現(xiàn)分解效果圖

1.實(shí)現(xiàn)Tab的切換效果

上面我們分析了菇绵,需要實(shí)現(xiàn)Tab底部的效果主要是繼承RadioButton肄渗,并在onDraw()方法中繪制一個(gè)矩形就可以了,還是直接上代碼吧咬最。

    @Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    int height = getMeasuredHeight();
    //the indicator's height
    int indicatorHeight = getResources().getDimensionPixelSize(R.dimen.radio_button_indicator_height);
    if (isChecked()){
        mPaint.setColor(getCurrentTextColor());
    }else {
        mPaint.setColor(Color.TRANSPARENT);
    }
    canvas.drawRect(0, height - indicatorHeight, getMeasuredWidth(), height, mPaint);
}

這里我們?cè)O(shè)置了選中色塊的高度翎嫡,再調(diào)用drawRect()方法繪制矩形(選中色塊)。這里簡單介紹一個(gè)這個(gè)方法

drawRect(float left, float top, float right, float bottom, @NonNull Paint paint)

先看看下面的圖解吧永乌。

矩形繪制示意圖

結(jié)合上面的圖解惑申,我們這里把top的值設(shè)為height - indicatorHeight,這個(gè)height為整個(gè)RadioButton的高度翅雏,由圖應(yīng)該可以很清楚的知道top的坐標(biāo)計(jì)算方法了圈驼。我們?cè)赗adioButton選中的時(shí)候通過Paint的值設(shè)置選中顏色,當(dāng)處于未選中狀態(tài)時(shí)設(shè)置為透明色望几,并且繪制同一塊區(qū)域绩脆,達(dá)到切換的效果。

2.實(shí)現(xiàn)Seekbar

在分析Seekbar的時(shí)候,我們把它分解成了幾個(gè)層次的疊加靴迫,那接下來我們的任務(wù)就是實(shí)現(xiàn)這些層次的疊加祈坠。我們這部分是通過現(xiàn)有的UI控件實(shí)現(xiàn)這個(gè)效果的,那么我們用哪一個(gè)UI控件可以實(shí)現(xiàn)層次的疊加呢矢劲,那當(dāng)然只有FrameLayou和RelativeLayout啦赦拘!那到底用FrameLayout還是RelativeLayout呢?這里我們使用RelativeLayout芬沉,對(duì)于FrameLayout的使用可以自行實(shí)踐躺同。

(1)實(shí)現(xiàn)第一層圓角——progressbar

第一條進(jìn)度

那我們這一層用什么實(shí)現(xiàn)呢,這里我們可以使用Andorid UI控件中的LinearLayout丸逸、FrameLayou等都可以蹋艺,但是我們這里使用ViewGroup的子類——LinearLayout(當(dāng)然也可以選取其他的),這樣我們可以對(duì)這一層進(jìn)行擴(kuò)展黄刚,在里面添加TextView捎谨、ImageView等。那么怎么實(shí)現(xiàn)圓角呢憔维,當(dāng)時(shí)是使用drawble進(jìn)行配置的涛救,但是對(duì)于使用drawable進(jìn)行配置是有問題的——不能代碼控制圓角的大小,這個(gè)問題導(dǎo)致可擴(kuò)展性太差业扒。那怎么解決呢检吆,當(dāng)然得想辦法啊,最后找到了使用GradientDrawable程储,這里我想說的就是蹭沛,我們?cè)谧远x的過程中總會(huì)出現(xiàn)問題,然后不停的找到具體的解決方法章鲤,一步一步實(shí)現(xiàn)摊灭。其實(shí)其他的編程問題都是這樣的。好了败徊,我們這里還是直接看看具體代碼吧帚呼,如下:

    mFirstBar = new LinearLayout(context);
    GradientDrawable drawable = new GradientDrawable();
    drawable.setColor(Color.parseColor("#FFBB33"));
    drawable.setCornerRadius((float) mProgressHeight / 2);
    mFirstBar.setBackgroundDrawable(drawable);
    //mFirstBar.setBackgroundResource(R.drawable.firtbar_bkg);
    mFirstBarLp = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, mProgressHeight);
    mFirstBarLp.addRule(CENTER_IN_PARENT);
    mFirstBar.setClickable(false);
    addView(mFirstBar, mFirstBarLp);

好了,第一層的實(shí)現(xiàn)應(yīng)該很簡單吧集嵌。

(2)實(shí)現(xiàn)第二層圓角——secondProgressbar

第二條進(jìn)度

其實(shí)secondProgressbar的實(shí)現(xiàn)基本上同progressbar的實(shí)現(xiàn)類似萝挤,還是直接上代碼吧

    mSecondBar = new LinearLayout(context);
    GradientDrawable secondDrawable = new GradientDrawable();
    secondDrawable.setColor(Color.parseColor("#99CC00"));
    secondDrawable.setCornerRadius((float)mProgressHeight/2);
    mSecondBar.setBackgroundDrawable(secondDrawable);
    mSecondBarLp = new       RelativeLayout.LayoutParams(mThumbRadius, mProgressHeight);
  //mSecondBarLp.leftMargin = 0;
    mSecondBarLp.addRule(CENTER_VERTICAL);
  //mSecondBarLp.addRule(LEFT_OF, THUMB_ID);
    mSecondBarLp.addRule(ALIGN_PARENT_LEFT);
    addView(mSecondBar, 1, mSecondBarLp);
    mSecondBar.setClickable(false);

這里我們看到了,在創(chuàng)建LayoutParams的時(shí)候根欧,我們?cè)O(shè)置的width的大小為圓角半徑的大小怜珍,為什么要這么做?這里還是先解釋一下吧凤粗,這樣做的目的主要是將secondProgressbar的最右端總是在thumb的中心位置酥泛,這里記住這一點(diǎn)后面我們?cè)诮榻B今豆。

(3)實(shí)現(xiàn)游標(biāo)——thumb

其實(shí)thumb很簡單啦,就是一個(gè)圓形的View柔袁。這里直接使用TextView呆躲,不要問我為什么——我任性$_$。直接看代碼吧捶索。

    mThumb = new TextView(context);
    GradientDrawable thumb = new GradientDrawable();
    thumb.setColor(Color.parseColor("#33b5e5"));
    thumb.setCornerRadius((float)mThumbRadius);
    mThumb.setBackgroundDrawable(thumb);
    mThumbLp = new RelativeLayout.LayoutParams(mThumbRadius*2, mThumbRadius*2);
    mThumbLp.addRule(CENTER_VERTICAL);
    mThumbLp.addRule(ALIGN_PARENT_LEFT);
    mThumbLp.leftMargin = 0;
    addView(mThumb, mThumbLp);
    mThumb.setId(THUMB_ID);
    mThumb.setGravity(Gravity.CENTER);
    if (mThumbTextSize != 0)
    mThumb.setTextSize(mThumbTextSize);

這里在我們使用TextView插掂,我們還可以在上面顯示一些信息,擴(kuò)展性更好腥例,當(dāng)然你也可以利用其他UI控件辅甥,設(shè)置你需要的UI!

(4)實(shí)現(xiàn)滑動(dòng)——Seekbar

好了燎竖,上面我們分解的圖都實(shí)現(xiàn)了璃弄,現(xiàn)在應(yīng)該要實(shí)現(xiàn)滑動(dòng)了吧!那么怎么實(shí)現(xiàn)按住Thumb就會(huì)滑動(dòng)构回,并且secondProgressbar和滑動(dòng)夏块,同時(shí)顯示當(dāng)前的進(jìn)度。當(dāng)然是使用監(jiān)聽OnTouchEvent事件啦纤掸,根據(jù)滑動(dòng)的distance不斷更新thumb和secondProgressbar的參數(shù)脐供,讓他們動(dòng)起來。那這里就直接實(shí)現(xiàn)RelativeLayout的OnTouchEvent方法茁肠,還是先看代碼吧患民,如下:

@Override
public boolean onTouchEvent(MotionEvent event) {
    int action = MotionEventCompat.getActionMasked(event);
    boolean isDraged = false;
    Rect rect = new Rect();
    mThumb.getHitRect(rect);
    switch (action){
        case MotionEvent.ACTION_DOWN:
            float x = event.getX();
            float y = event.getY();
            boolean contain = rect.contains((int)x, (int)y);
            if (contain){
                mLastMotionX = event.getX();
                isDraged = true;
            }
            break;
        case MotionEvent.ACTION_MOVE:
            dragThumb(event.getX());
            break;

        case MotionEvent.ACTION_UP:

            break;
    }
    return isDraged;
}

private void dragThumb(float x){
    float distance = (x - mLastMotionX);
    mLastMotionX = x;
    mThumbLp.leftMargin = (int) (mThumbLp.leftMargin + distance);
    mSecondBarLp.width = (int) (mSecondBarLp.width + distance);
    LogUtils.LogD(TAG, " horizontal current distance == " + distance);
    //confirm this thumb is show, no anywhere is hide
    if (mThumbLp.leftMargin <= 0) {
        mThumbLp.leftMargin = 0;
        mSecondBarLp.width = mThumbRadius;
    } else if (mThumbLp.leftMargin >= getMeasuredWidth() - mThumbRadius * 2) {
        mThumbLp.leftMargin = getMeasuredWidth() - mThumbRadius * 2;
        mSecondBarLp.width = getMeasuredWidth() - mThumbRadius;
    }
    updateViewLayout(mThumb, mThumbLp);
    updateViewLayout(mSecondBar, mSecondBarLp);
}

這里我們實(shí)現(xiàn)的是整個(gè)RelativeLayout的OnTouchEvent方法缩举,所以它的touch事件是針對(duì)整個(gè)RelativeLayout的垦梆。所以這里我們要做一下過濾,點(diǎn)擊范圍在不在thumb上面仅孩,只有點(diǎn)擊和拖動(dòng)都在thumb上面這次的touch對(duì)thumb才有效托猩。當(dāng)確定拖動(dòng)有效的時(shí)候,在開始初始化的時(shí)候辽慕,設(shè)置了thumb相對(duì)于父控件為ALIGN_PARENT_LEFT京腥,所以通過改變mThumbLp.leftMargin就可以改變thumb于左邊的距離啦。對(duì)于secondProgressbar溅蛉,只要改變mSecondBarLp.width的大小就可以改變它的寬度公浪,最后調(diào)用updateViewLayout()方法更新UI。這里我們要注意兩點(diǎn):
1.防止thumb滑動(dòng)到最右端時(shí)超出邊界船侧。
2.防止thumb滑動(dòng)回來到最左端時(shí)超出邊界欠气。

好了,到這里通過簡單的介紹镜撩,我們將這個(gè)Seekbar的基本功能完成预柒。

總結(jié)

上面我們利用Android控件實(shí)現(xiàn)了一個(gè)簡單的Seekbar,現(xiàn)在簡單的總結(jié)一下:

1.和實(shí)踐自定UI—View的時(shí)候一樣,我們還是把這個(gè)Seekbar進(jìn)行了分解宜鸯,然后一步一步實(shí)現(xiàn)憔古。

2.在自定義的過程中我們會(huì)遇到很多問題,我在這里遇到了這些問題:圓角怎么可以用代碼控制淋袖、在開始的時(shí)候我沒有利用RelativeLayou的onTouchEvent方法實(shí)現(xiàn)滑動(dòng)鸿市,而是將onTouchEvent事件直接set在thumb上面,結(jié)果滑動(dòng)的時(shí)候出現(xiàn)了問題(有興趣的可以自己試試看看是什么問題)....即碗。這里面遇到了很多問題灸芳,但都一個(gè)一個(gè)擊破,所以我們?cè)谧远║I的時(shí)候不要心急拜姿,一點(diǎn)一點(diǎn)將沒有問題解決烙样,最后就會(huì)實(shí)現(xiàn)你想要的效果。

3.這里只是通過這個(gè)例子分析怎樣去利用Android UI 自定義我們自己需要的UI蕊肥。這里只是引導(dǎo)谒获,更多的還是靠實(shí)踐、實(shí)踐壁却、實(shí)踐...批狱。重要的事說三遍_

好了,國際慣例展东,可以自己練習(xí)一下垂直方向的Seekbar赔硫,如下圖:

垂直效果

最后還是放上代碼地址吧

如何利用View自定義UI請(qǐng)閱讀實(shí)踐自定UI—View

如何利用ViewGroup自定義UI請(qǐng)閱讀實(shí)踐自定義UI-ViewGroup

希望在Android學(xué)習(xí)的路上,大家共同成長盐肃!

如果你也在簡書上撰寫Android相關(guān)的內(nèi)容爪膊,或者也準(zhǔn)備寫可以加入我們的寫作交流群:196537830

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市砸王,隨后出現(xiàn)的幾起案子推盛,更是在濱河造成了極大的恐慌,老刑警劉巖谦铃,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耘成,死亡現(xiàn)場離奇詭異,居然都是意外死亡驹闰,警方通過查閱死者的電腦和手機(jī)瘪菌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嘹朗,“玉大人师妙,你說我怎么就攤上這事÷庀裕” “怎么了疆栏?”我有些...
    開封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵曾掂,是天一觀的道長。 經(jīng)常有香客問我壁顶,道長珠洗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任若专,我火速辦了婚禮许蓖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘调衰。我一直安慰自己膊爪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開白布嚎莉。 她就那樣靜靜地躺著米酬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪趋箩。 梳的紋絲不亂的頭發(fā)上赃额,一...
    開封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天,我揣著相機(jī)與錄音叫确,去河邊找鬼跳芳。 笑死,一個(gè)胖子當(dāng)著我的面吹牛竹勉,可吹牛的內(nèi)容都是我干的飞盆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼次乓,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼吓歇!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起檬输,我...
    開封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤照瘾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后丧慈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡主卫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年逃默,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片簇搅。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡完域,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瘩将,到底是詐尸還是另有隱情吟税,我是刑警寧澤凹耙,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站肠仪,受9級(jí)特大地震影響肖抱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜异旧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一意述、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吮蛹,春花似錦荤崇、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至每篷,卻和暖如春喜每,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背雳攘。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來泰國打工带兜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人吨灭。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓刚照,卻偏偏與公主長得像,于是被迫代替她去往敵國和親喧兄。 傳聞我的和親對(duì)象是個(gè)殘疾皇子无畔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,288評(píng)論 25 707
  • PHP 是服務(wù)器端腳本語言; PHP(全稱:PHP:Hypertext Preprocessor吠冤,即"PHP:超文...
    wyude閱讀 487評(píng)論 2 5
  • 正向意圖: 5.22本周最想要收獲的是喜悅浑彰, 最想要?jiǎng)?chuàng)造的是接納, 最想要給出的是支持[愉快]拯辙。 好種子開花:感恩...
    星光2017閱讀 270評(píng)論 0 3
  • (該文章原創(chuàng)郭变,作者國旅學(xué)院16土地資源管理蔣思雨) 推銷是一種門檻低、彈性大的職業(yè)涯保。這個(gè)職業(yè)對(duì)于求...
    多動(dòng)腦子閱讀 370評(píng)論 0 0
  • 姓名:陳權(quán) 公司:青檸養(yǎng)車 【知~學(xué)習(xí)】 《六項(xiàng)精進(jìn)》大綱56遍 共89遍 《領(lǐng)導(dǎo)者的資質(zhì)》音頻打卡第5天 《輕課...
    水青檸閱讀 145評(píng)論 0 0