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>