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>