打造通用的下拉列表
阿新 • • 發佈:2018-12-16
這是一個通用的下拉列表效果,型別和價格展示的是一個textview,品牌和更多展示的是一個RecyclerView列表效果;這裡是採用adapter設計模式實現的。
實現思路:
1、可以模仿ListView的BaseAdapter那樣建立一個抽象的adapter,後面的適配將繼承自該抽象類;
2、自定義佈局容易,將tab欄,選單內容、背影逐一新增到佈局容器中;
3、實現下拉和收縮效果,併為其新增動畫效果;
4、利用觀察者模式實現點選選單內容關閉,並進行結果回撥;
先按照第一步新建一個抽象的adapter:
接下來可以實現第二步和第三步,自定義佈局容器並將tab、選單內容、背景新增到佈局容器中;/** * Created by Administrator on 2018/1/22. * 篩選選單的adapter */ public abstract class BaseMenuAdapter { //選單內容點選關閉的觀察者 private MenuObserver mObserver; //註冊觀察者 public void registerDataSetObserver(MenuObserver observer) { mObserver = observer; } // 登出觀察者 public void unregisterDataSetObserver(MenuObserver observer) { mObserver = null; } /** * 關閉選單 */ public void closeMenu(){ if(mObserver != null){ mObserver.closeMenu(); } } /** * 獲取總共有多少條 * * @return */ public abstract int getCount(); /** * 獲取tab內容 * * @param position * @param parent * @return */ public abstract View getTabView(int position, ViewGroup parent); /** * 獲取當前選單的內容 * * @param position * @param parent * @return */ public abstract View getMenuView(int position, ViewGroup parent); /** * 選單開啟 * * @param tabView */ public void menuOpen(View tabView) { } /** * 選單關閉 * * @param tabView */ public void menuClose(View tabView) { } }
收縮和展開下拉的動畫也就直接在裡面實現了;接下來就是監聽選單內容的點選及回撥,還有就是當點選選單內容後,選單內容也要關閉掉;點選動作是ListMenuAdapter發生的,關閉選單內容是在ListDataScreenView中發生的,是在兩個不同的類中,這裡實現的方式有兩種:public class ListDataScreenView extends LinearLayout implements View.OnClickListener { //頭部tab private LinearLayout mMenuTabView; private Context mContext; //中間選單內容 private FrameLayout mMenuMiddleView; //陰影 private View mShadowView; //陰影的背景顏色 private int mShadowColor = 0x88888888; //建立選單用來存放選單內容 private FrameLayout mMenuContentView; //選單的adapter private BaseMenuAdapter mAdapter; //內容選單的高度 private int menuContainerHeight; //當前的開啟的位置 private int mCurentPosition = -1; private long DURATIOIN_TIME = 350; //動畫是否在執行 private boolean mAnimatorExecute; public ListDataScreenView(Context context) { this(context, null); } public ListDataScreenView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ListDataScreenView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mContext = context; initLayout(); } /** * 初始化佈局 */ private void initLayout() { setOrientation(VERTICAL); //1.1建立頭部用來存放tab mMenuTabView = new LinearLayout(mContext); mMenuTabView.setBackgroundColor(Color.GREEN); mMenuTabView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); addView(mMenuTabView); //1.2建立FramLayout用來存放選單內容佈局+陰影 mMenuMiddleView = new FrameLayout(mContext); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0); params.weight = 1; mMenuMiddleView.setLayoutParams(params); addView(mMenuMiddleView); //建立陰影,可以不用設定LayoutParams 預設就是MATCH_PARENT mShadowView = new View(mContext); mShadowView.setBackgroundColor(mShadowColor); mShadowView.setAlpha(0f); mShadowView.setOnClickListener(this); mShadowView.setVisibility(GONE); mMenuMiddleView.addView(mShadowView); //建立選單用來存放選單內容 mMenuContentView = new FrameLayout(mContext); mMenuContentView.setBackgroundColor(Color.WHITE); mMenuMiddleView.addView(mMenuContentView); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int height = MeasureSpec.getSize(heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (menuContainerHeight == 0 && height > 0) { //內容的高度應該不是全部,應該是整個View的75% menuContainerHeight = (int) (height * 75f / 100); ViewGroup.LayoutParams params = mMenuContentView.getLayoutParams(); params.height = menuContainerHeight; mMenuContentView.setLayoutParams(params); //進來的時候陰影不顯示 內容也是不顯示的 mMenuContentView.setTranslationY(-menuContainerHeight); } } /** * 具體的觀察者 */ private class AdapterDataSetObserver extends MenuObserver { @Override public void closeMenu() { //如果有註冊就會收到通知 ListDataScreenView.this.closeMenu(); } } private AdapterDataSetObserver mMenuObserver; /** * 設定adapter * * @param adapter */ public void setAdapter(BaseMenuAdapter adapter) { if (adapter == null) { return; } if (mAdapter != null && mMenuObserver != null) { //取消訂閱 mAdapter.unregisterDataSetObserver(mMenuObserver); } //註冊一個觀察者 this.mAdapter = adapter; mMenuObserver = new AdapterDataSetObserver(); mAdapter.registerDataSetObserver(mMenuObserver); //觀察者 this.mAdapter = adapter; //獲取有多少條 int count = mAdapter.getCount(); for (int i = 0; i < count; i++) { //獲取選單的tab View tabView = mAdapter.getTabView(i, mMenuTabView); mMenuTabView.addView(tabView); //寬度不是等寬,weight設定為1 LinearLayout.LayoutParams params = (LayoutParams) tabView.getLayoutParams(); params.weight = 1; tabView.setLayoutParams(params); //設定點選事件 setTabClick(tabView, i); //獲取選單內容 View menuView = mAdapter.getMenuView(i, mMenuContentView); //先隱藏,當點選的時候才顯示 menuView.setVisibility(GONE); mMenuContentView.addView(menuView); } } /** * 點選事件處理 * * @param tabView * @param position */ private void setTabClick(final View tabView, final int position) { tabView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (mCurentPosition == -1) { //沒有開啟 openMenu(position, tabView); } else { if (mCurentPosition == position) { //開啟關閉 closeMenu(); } else { //切換顯示 View currentMenu = mMenuContentView.getChildAt(mCurentPosition); currentMenu.setVisibility(GONE); mAdapter.menuClose(mMenuTabView.getChildAt(mCurentPosition)); mCurentPosition = position; currentMenu = mMenuContentView.getChildAt(mCurentPosition); currentMenu.setVisibility(VISIBLE); mAdapter.menuOpen(mMenuTabView.getChildAt(mCurentPosition)); } } } }); } /** * 關閉menu */ public void closeMenu() { if (mAnimatorExecute) { return; } //開啟開啟動畫 位移 透明度 ObjectAnimator translationAnimator = ObjectAnimator.ofFloat(mMenuContentView, "translationY", 0, -menuContainerHeight); translationAnimator.setDuration(DURATIOIN_TIME); translationAnimator.start(); ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(mShadowView, "alpha", 1f, 0); alphaAnimator.setDuration(DURATIOIN_TIME); //要等關閉動畫執行完才能去隱藏選單 alphaAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); View menuView = mMenuContentView.getChildAt(mCurentPosition); menuView.setVisibility(GONE); mCurentPosition = -1; mShadowView.setVisibility(GONE); mAnimatorExecute = false; } @Override public void onAnimationStart(Animator animation) { super.onAnimationStart(animation); mAnimatorExecute = true; mAdapter.menuClose(mMenuTabView.getChildAt(mCurentPosition)); } }); alphaAnimator.start(); } /** * 開啟menu * * @param position */ public void openMenu(final int position, final View tabView) { if (mAnimatorExecute) { return; } mShadowView.setVisibility(VISIBLE); //獲取當前位置,去顯示當前選單 View menuView = mMenuContentView.getChildAt(position); menuView.setVisibility(VISIBLE); //開啟開啟動畫 位移 透明度 ObjectAnimator translationAnimator = ObjectAnimator.ofFloat(mMenuContentView, "translationY", -menuContainerHeight, 0); translationAnimator.setDuration(DURATIOIN_TIME); translationAnimator.start(); ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(mShadowView, "alpha", 0, 1f); alphaAnimator.setDuration(DURATIOIN_TIME); alphaAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { super.onAnimationStart(animation); mAnimatorExecute = true; mAdapter.menuOpen(tabView); } @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); mCurentPosition = position; mAnimatorExecute = false; } }); alphaAnimator.start(); } @Override public void onClick(View v) { closeMenu(); } }
第一種:在時候的時候將ListDataScreenView作為引數通過ListMenuAdapter的構造方法傳入,然後觸發點選的時候呼叫ListDataScreenView裡面的關閉選單內容方法,進行關閉;
第二種:模仿ListView那樣使用觀察者方式實現選單內容的關閉;
這裡採用的是第二種方式,建立一個抽象的觀察者;
/**
* Created by Administrator on 2018/1/25.
* 選單內容關閉的觀察者
*/
public abstract class MenuObserver {
public abstract void closeMenu();
}
在BaseMenuAdapter中提供註冊觀察者(registerDataSetObserver(MenuObserver observer))和登出觀察者(unregisterDataSetObserver(MenuObserver observer))在setAdapter中進行註冊和登出的呼叫,這樣就實現了,直接使用就可以了;
<?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:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.listviewchoice.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!內容"
android:layout_centerInParent="true"/>
<com.listviewchoice.ListDataScreenView
android:id="@+id/list_data_screen_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
public class MainActivity extends AppCompatActivity {
private ListDataScreenView screenView;
private ListMenuAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
List<String> list=getData();
screenView = (ListDataScreenView) findViewById(R.id.list_data_screen_view);
adapter = new ListMenuAdapter(this,list);
screenView.setAdapter(adapter);
adapter.setListener(new ListMenuAdapter.ListMenuListener() {
@Override
public void listMenuClick(List<String> mItems, int position) {
Toast.makeText(MainActivity.this,mItems.get(position),Toast.LENGTH_LONG).show();
}
});
}
public List<String> getData() {
List<String> data=new ArrayList<>();
data.add("型別");
data.add("品牌");
data.add("價格");
data.add("更多");
return data;
}
}
在建立ListDataScreenView的介面卡的時候需要注意getMenuView(final int position, ViewGroup parent)方法,需要根據自己的需要去例項化佈局檔案,並將其新增進來;
public class ListMenuAdapter extends BaseMenuAdapter {
private List<String> mItems;
private Context mContext;
private ListMenuListener listener;
public ListMenuAdapter( Context mContext,List<String> mItems) {
this.mItems = mItems;
this.mContext = mContext;
}
@Override
public int getCount() {
return mItems.size();
}
@Override
public View getTabView(final int position, ViewGroup parent) {
final View tabView = LayoutInflater.from(mContext).inflate(R.layout.ui_list_data_screen_tab, parent, false);
TextView tabViewTv = (TextView) tabView.findViewById(R.id.tab_view_tv);
tabViewTv.setTextColor(Color.BLACK);
tabViewTv.setText(mItems.get(position));
return tabView;
}
@Override
public View getMenuView(final int position, ViewGroup parent) {
String mItem = mItems.get(position);
View menuView = null;
if(mItem.equals("品牌")||mItem.equals("更多")){
menuView = LayoutInflater.from(mContext).inflate(R.layout.ui_list_data_screen_menu_list, parent, false);
final List<String> data = getData(mItem);
RecyclerView recyclerView = (RecyclerView) menuView.findViewById(R.id.recycler_view);
//設定佈局管理器
recyclerView.setLayoutManager(new LinearLayoutManager(mContext));
ListContentAdapter contentAdapter=new ListContentAdapter(mContext,data);
//設定adapter
recyclerView.setAdapter(contentAdapter);
contentAdapter.setListener(new ListContentAdapter.OnItemClicListener() {
@Override
public void listMenuClick(List<String> list, int position) {
//觀察者
closeMenu();
if (listener != null) {
listener.listMenuClick(data, position);
}
}
});
}else{
menuView = LayoutInflater.from(mContext).inflate(R.layout.ui_list_data_screen_menu, parent, false);
TextView menuViewTv = (TextView) menuView.findViewById(R.id.menu_view_tv);
menuViewTv.setText(mItem);
//點選事件
menuViewTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//觀察者
closeMenu();
if (listener != null) {
listener.listMenuClick(mItems, position);
}
}
});
}
return menuView;
}
private List<String> getData(String mItem) {
List<String> list=new ArrayList<>();
if(mItem.equals("品牌")){
for (int i=0;i<20;i++){
list.add("品牌"+i);
}
}else{
for (int i=0;i<20;i++){
list.add("更多"+i);
}
}
return list;
}
@Override
public void menuOpen(View tabView) {
super.menuOpen(tabView);
TextView tabTv = (TextView) tabView;
tabTv.setTextColor(Color.RED);
}
@Override
public void menuClose(View tabView) {
super.menuClose(tabView);
TextView tabTv = (TextView) tabView;
tabTv.setTextColor(Color.BLACK);
}
public void setListener(ListMenuListener listener) {
this.listener = listener;
}
interface ListMenuListener {
void listMenuClick(List<String> list, int position);
}
}
下面這個的話是RecyclerView的介面卡;
public class ListContentAdapter extends RecyclerView.Adapter<ListContentAdapter.ContentHolder>{
private Context context;
private List<String> datas;
private OnItemClicListener listener;
public ListContentAdapter(Context context, List<String> datas) {
this.context = context;
this.datas = datas;
}
@Override
public ContentHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
ContentHolder holder=new ContentHolder(view);
return holder;
}
@Override
public void onBindViewHolder(ContentHolder holder, final int position) {
holder.item.setText(datas.get(position));
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(listener!=null){
listener.listMenuClick(datas,position);
}
}
});
}
@Override
public int getItemCount() {
return datas.size();
}
class ContentHolder extends RecyclerView.ViewHolder{
TextView item;
public ContentHolder(View itemView) {
super(itemView);
item= (TextView) itemView.findViewById(R.id.item);
}
}
public void setListener(OnItemClicListener listener) {
this.listener = listener;
}
interface OnItemClicListener{
void listMenuClick(List<String> list, int position);
}
}
這樣子整個的大致效果就實現了,下面是原始碼的連結,可以根據專案的需要對原始碼進行修改。
原始碼地址:
https://pan.baidu.com/s/1nwI0GvZ