实践自定义UI—RLF...(RelativeLayout LinearLayout FrameLayout....)
效果圖
分解效果圖
?我們看到Horizontal和Vertical兩個Tab是不可以滑動的,只有通過點擊來觸發左右切換,在Tab的下面會有一個帶顏色的bar標志當前選中的位置。想這樣的UI目前現有的Android控件應該沒有可以直接使用的(據我的了解)。一般我們在實現這樣的UI時,我們會用TextView和一個帶顏色的View組合實現。這樣好像也可以,但是還是有一定的代碼量的。那么我們怎么用最少的代碼實現這樣的需求呢!首先我們看到左右切換的選中,類似于RadioButton這樣的控件——在多個選項中只能選取其中一個,但是RadioButton這樣的控件底部好像也沒有這樣的帶顏色的bar啊,難道還是要利用一個View和它組合使用嗎,那這樣還是太low了。我們想啊,既然底部沒有這樣一個bar,那我們可以讓RadioButton在底部畫一個啊。怎么畫?那當然是繼承它,在onDraw()方法里面畫啦。具體怎么畫,我相信畫一個矩形應該很簡單吧^_^。
?上面分析完了Tab的部分,這下我們來看看這個Seekbar吧,我們先看看下面分解的4張圖片。
第一條進度條
第二條進度條
游標
以上三個疊加效果
從上面的四張圖片可以看出,這個Seekbar是通過前面三張一個一個疊加過后達到第四張圖片的效果。當我們拖動小thumb(小圓球)滑動的時候,不斷的改變第二層上圓角矩形的寬度就可以達到想要的效果了。那么具體怎么實現,當然還是上代碼啦。
實現分解效果圖
1.實現Tab的切換效果
?上面我們分析了,需要實現Tab底部的效果主要是繼承RadioButton,并在onDraw()方法中繪制一個矩形就可以了,還是直接上代碼吧。
@Override protected void onDraw(Canvas canvas) {super.onDraw(canvas);int height = getMeasuredHeight();//the indicator's heightint 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); }這里我們設置了選中色塊的高度,再調用drawRect()方法繪制矩形(選中色塊)。這里簡單介紹一個這個方法
drawRect(float left, float top, float right, float bottom, @NonNull Paint paint)先看看下面的圖解吧。
矩形繪制示意圖
結合上面的圖解,我們這里把top的值設為height - indicatorHeight,這個height為整個RadioButton的高度,由圖應該可以很清楚的知道top的坐標計算方法了。我們在RadioButton選中的時候通過Paint的值設置選中顏色,當處于未選中狀態時設置為透明色,并且繪制同一塊區域,達到切換的效果。
2.實現Seekbar
?在分析Seekbar的時候,我們把它分解成了幾個層次的疊加,那接下來我們的任務就是實現這些層次的疊加。我們這部分是通過現有的UI控件實現這個效果的,那么我們用哪一個UI控件可以實現層次的疊加呢,那當然只有FrameLayou和RelativeLayout啦!那到底用FrameLayout還是RelativeLayout呢?這里我們使用RelativeLayout,對于FrameLayout的使用可以自行實踐。
(1)實現第一層圓角——progressbar
第一條進度
?那我們這一層用什么實現呢,這里我們可以使用Andorid UI控件中的LinearLayout、FrameLayou等都可以,但是我們這里使用ViewGroup的子類——LinearLayout(當然也可以選取其他的),這樣我們可以對這一層進行擴展,在里面添加TextView、ImageView等。那么怎么實現圓角呢,當時是使用drawble進行配置的,但是對于使用drawable進行配置是有問題的——不能代碼控制圓角的大小,這個問題導致可擴展性太差。那怎么解決呢,當然得想辦法啊,最后找到了使用GradientDrawable,這里我想說的就是,我們在自定義的過程中總會出現問題,然后不停的找到具體的解決方法,一步一步實現。其實其他的編程問題都是這樣的。好了,我們這里還是直接看看具體代碼吧,如下:
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);好了,第一層的實現應該很簡單吧。
(2)實現第二層圓角——secondProgressbar
第二條進度
?其實secondProgressbar的實現基本上同progressbar的實現類似,還是直接上代碼吧
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);這里我們看到了,在創建LayoutParams的時候,我們設置的width的大小為圓角半徑的大小,為什么要這么做?這里還是先解釋一下吧,這樣做的目的主要是將secondProgressbar的最右端總是在thumb的中心位置,這里記住這一點后面我們在介紹。
(3)實現游標——thumb
?其實thumb很簡單啦,就是一個圓形的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,我們還可以在上面顯示一些信息,擴展性更好,當然你也可以利用其他UI控件,設置你需要的UI!
(4)實現滑動——Seekbar
?好了,上面我們分解的圖都實現了,現在應該要實現滑動了吧!那么怎么實現按住Thumb就會滑動,并且secondProgressbar和滑動,同時顯示當前的進度。當然是使用監聽OnTouchEvent事件啦,根據滑動的distance不斷更新thumb和secondProgressbar的參數,讓他們動起來。那這里就直接實現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 hideif (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); }這里我們實現的是整個RelativeLayout的OnTouchEvent方法,所以它的touch事件是針對整個RelativeLayout的。所以這里我們要做一下過濾,點擊范圍在不在thumb上面,只有點擊和拖動都在thumb上面這次的touch對thumb才有效。當確定拖動有效的時候,在開始初始化的時候,設置了thumb相對于父控件為ALIGN_PARENT_LEFT,所以通過改變mThumbLp.leftMargin就可以改變thumb于左邊的距離啦。對于secondProgressbar,只要改變mSecondBarLp.width的大小就可以改變它的寬度,最后調用updateViewLayout()方法更新UI。這里我們要注意兩點:
1.防止thumb滑動到最右端時超出邊界。
2.防止thumb滑動回來到最左端時超出邊界。
好了,到這里通過簡單的介紹,我們將這個Seekbar的基本功能完成。
總結
?上面我們利用Android控件實現了一個簡單的Seekbar,現在簡單的總結一下:
1.和實踐自定UI—View的時候一樣,我們還是把這個Seekbar進行了分解,然后一步一步實現。
2.在自定義的過程中我們會遇到很多問題,我在這里遇到了這些問題:圓角怎么可以用代碼控制、在開始的時候我沒有利用RelativeLayou的onTouchEvent方法實現滑動,而是將onTouchEvent事件直接set在thumb上面,結果滑動的時候出現了問題(有興趣的可以自己試試看看是什么問題)....。這里面遇到了很多問題,但都一個一個擊破,所以我們在自定UI的時候不要心急,一點一點將沒有問題解決,最后就會實現你想要的效果。
3.這里只是通過這個例子分析怎樣去利用Android UI 自定義我們自己需要的UI。這里只是引導,更多的還是靠實踐、實踐、實踐...。重要的事說三遍^_^
好了,國際慣例,可以自己練習一下垂直方向的Seekbar,如下圖:
垂直效果
?最后還是放上代碼地址吧
如何利用View自定義UI請閱讀實踐自定UI—View
如何利用ViewGroup自定義UI請閱讀實踐自定義UI-ViewGroup
希望在Android學習的路上,大家共同成長!
總結
以上是生活随笔為你收集整理的实践自定义UI—RLF...(RelativeLayout LinearLayout FrameLayout....)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 实践自定义UI-ViewGroup
- 下一篇: 教你搞定Android自定义ViewGr