1. 程式人生 > 其它 >Android UI:篩選條的簡單實現

Android UI:篩選條的簡單實現

FilterBar

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-khcpYsrJ-1591256127287)(https://github.com/XuNeverMore/FilterBar/raw/master/filterbar.gif)]

這個篩選條比較常用吧,做專案遇到過,不過這塊不是我寫的,閒來沒事做個簡單封裝。我覺得重點在這個箭頭上,畢竟能動起來的箭頭更酷炫,文字顏色切換沒什麼好說的。之前看過一個github上的專案,僅僅用一個textView,然後drawableright屬性使用的是rotatedrawable.setLevel使箭頭轉動,很有創意。但我就怕UI把這個箭頭大小沒弄好,drawableRight就不好控制箭頭大小了,所以用的是textview+imageView。

FilterTab

佈局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:gravity="center_horizontal"
    android:layout_height="match_parent">

    <TextView
        android:duplicateParentState="true"
        android:id="@+id/filter_text"
        android:layout_centerVertical="true"
        tools:text="綜合排序"
        style="@style/filterTextStyle" />
    <ImageView
        android:layout_centerVertical="true"
        android:id="@+id/filter_img"
        style="@style/filterImageStyle" />

</RelativeLayout>

textview+image佈局很簡單,樣式寫成style,方便以後修改。android:duplicateParentState該屬性可以讓子view狀態和父view狀態一樣,字型顏色就可以用selector控制了。自定義一個屬性顯示篩選條件的文字,選中和取消選中的時候執行動畫,然後加了一個狀態選中的監聽。

程式碼:

public class FilterTab extends RelativeLayout {

    //旋轉動畫執行時間
    private static final long DURATION_ROTATE = 200;
    private TextView textFilter;
    private ImageView imgArrow;

    private TabSelectedObervable tabSelectedObervable = new TabSelectedObervable();


    public interface OnTabSelectedChangeListener{
        void onChange(FilterTab filterTab,boolean selected);
    }

    private  class TabSelectedObervable extends Observable<OnTabSelectedChangeListener>{

        public void notifyTabChanged(FilterTab filterTab,boolean selected){
                synchronized(mObservers) {
                    for (int i = mObservers.size() - 1; i >= 0; i--) {
                        mObservers.get(i).onChange(filterTab,selected);
                    }
                }
        }
    }



    public FilterTab(Context context) {
        this(context, null);
    }

    public FilterTab(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public FilterTab(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        LayoutInflater.from(context).inflate(R.layout.filter_tab, this);
        textFilter = (TextView) findViewById(R.id.filter_text);
        imgArrow = (ImageView) findViewById(R.id.filter_img);


        //設定篩選條件
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.FilterTab);
        String filterText = typedArray.getString(R.styleable.FilterTab_filterText);
        if(!TextUtils.isEmpty(filterText)){
            textFilter.setText(filterText);
        }
        typedArray.recycle();

    }


    /**
     * 設定篩選標籤當前篩選條件
     *
     * @param charSequence
     */
    public void setText(CharSequence charSequence) {
        textFilter.setText(charSequence);
    }


    /**
     * 設定選中狀態
     *
     * @param selected
     */
    public void setFilterTabSelected(boolean selected) {
        boolean selectedState = isSelected();


        if (selectedState && selected) {//去除無效的狀態
            return;
        }

        //設定切換選中狀態
        setSelected(selected);
        //改變箭頭方向
        rotateArrow(selected);


        //通知觀察者選中狀態改變
        tabSelectedObervable.notifyTabChanged(this,selected);

    }

    /**
     * 旋轉箭頭:選中true,箭頭向上,取消:false,箭頭向下
     *
     * @param up
     */
    private void rotateArrow(boolean up) {

        ObjectAnimator rotation = ObjectAnimator.ofFloat(imgArrow, "rotation", up?0f:180f, up?180f:360f);
        rotation.setInterpolator(new LinearOutSlowInInterpolator());
        rotation.setDuration(DURATION_ROTATE);
        rotation.start();

    }


    /**
     * 新增狀態改變的監聽
     * @param listener
     */
    public void addTabSelectedChangeListener(OnTabSelectedChangeListener listener){
        tabSelectedObervable.registerObserver(listener);
    }


    /**
     * 移除狀態改變的監聽
     * @param listener
     */
    public void removeTabSelectedChangeListener(OnTabSelectedChangeListener listener){
        tabSelectedObervable.unregisterObserver(listener);
    }

}

FilterBar

單個篩選條件寫好了,現在要考慮篩選條件之間的相互影響了。比如選中第一個FilterTab0,再選第二個FilterTab1的時候,FilterTab0就要取消選中,這點還是需要封裝下的,不然每次都要處理狀態很煩。想了下用一個線性佈局,作為FilterTabs的容器,當它們填充完畢的時候就把關係處理好。這就涉及每個FilterTab的狀態切換了,需要監聽,所以我是後來寫FiterBar才王FilterTab加了監聽方法的,有時候真的是水到渠成(想到這個詞,可能與原意不符,我說的意思就是需要的時候才自然而然會去做)。這個監聽事件是用了觀察者模式的,考慮到使用者可能也要加監聽,若用set就只能設定一個會覆蓋掉,所以自然而然就寫成addTabSelectedChangeListener了,跟viewpager.addOnPageChangeListener一樣,可以設定多個。


public class FilterBar extends LinearLayout implements FilterTab.OnTabSelectedChangeListener {
    public FilterBar(Context context) {
        this(context, null);
    }

    public FilterBar(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public FilterBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setOrientation(HORIZONTAL);

    }


    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        syncFilterTabState();
    }

    private void syncFilterTabState() {
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            if (child instanceof FilterTab) {
                ((FilterTab) child).addTabSelectedChangeListener(FilterBar.this);
            }
        }
    }


    private FilterTab lastSelectedTab;

    @Override
    public void onChange(FilterTab filterTab, boolean selected) {
        if (selected) {
            if (lastSelectedTab != null) {
                lastSelectedTab.setFilterTabSelected(false);
            }
            lastSelectedTab = filterTab;
        }else {
            lastSelectedTab = null;
        }

    }
}

到此結束。哦FilterBar繼承的是LinearLayout,所以分割線用showDivider就可以了。
activity_main佈局:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical">

    <com.xunevermore.filterbar.FilterBar
        android:id="@+id/filter_bar"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="#fff"
        android:divider="@drawable/divider_filter"
        android:dividerPadding="10dp"
        android:showDividers="middle">

        <com.xunevermore.filterbar.FilterTab
            android:id="@+id/filter_tab0"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_weight="1"
            android:background="#fff"
            app:filterText="綜合排序" />

        <com.xunevermore.filterbar.FilterTab
            android:id="@+id/filter_tab1"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_weight="1"
            android:background="#fff"
            app:filterText="產品成分" />

        <com.xunevermore.filterbar.FilterTab
            android:id="@+id/filter_tab2"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_weight="1"
            android:background="#fff"
            app:filterText="產品色系" />

        <com.xunevermore.filterbar.FilterTab
            android:id="@+id/filter_tab3"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_weight="1"
            android:background="#fff"
            app:filterText="篩選" />
    </com.xunevermore.filterbar.FilterBar>


</LinearLayout>

github 原始碼