内容简介:前面两章实现了筛选控件的内容,今天要来实现筛选控件的容器,首先看效果:点击上面的按钮可以展开隐藏菜单,需要有动画效果,重复点击同一个按钮可以toggle,菜单展开时点击另外的按钮需要先收起菜单再展开菜单,点击半透明蒙层可以收起菜单。很多初级开发者可能看到这个效果会立刻想到用PopupWindow去实现,然而PopupWindow去实现有很多坑,例如点击隐藏的控制、代码书写麻烦,处理生命周期状态保存麻烦,和现有的代码控件协调使用等等。
前面两章实现了筛选控件的内容,今天要来实现筛选控件的容器,首先看效果:
需求分析
点击上面的按钮可以展开隐藏菜单,需要有动画效果,重复点击同一个按钮可以toggle,菜单展开时点击另外的按钮需要先收起菜单再展开菜单,点击半透明蒙层可以收起菜单。
实现思路
很多初级开发者可能看到这个效果会立刻想到用PopupWindow去实现,然而PopupWindow去实现有很多坑,例如点击隐藏的控制、代码书写麻烦,处理生命周期状态保存麻烦,和现有的代码控件协调使用等等。
网上也也有一些实现,例如 dongjunkun / DropDownMenu ,阅读源码发现其代码陈旧,封装过于严密而缺乏灵活性,不建议用于生产环境。
按照kiss原则,我们的实现方式应该是简单的,我们采用的实现方式是移动View的方式实现菜单展开隐藏的效果。动画采用Animator
注意我们不采用动态修改View的高度去实现隐藏展开而是修改TranlationY是有原因的,如果动态修改高度会导致View的重新测量,像我们的View内部可能包含RecyclerView重新测量,会带来严重的性能问题
实现
- 首先定义一个自定义的ViewGroup,这个ViewGroup的背景色为灰色透明,相当于一个蒙层,他的子view我们通过DataBinding在调用的时候动态添加,这样保证了最大的灵活性:要呈现的内容完全开放给调用方,我们只负责展示和隐藏。
public class YokoView extends FrameLayout { public interface YokoAdapter { void onCollapsed(YokoView view); void onExpanded(YokoView view); } private View mView; private YokoAdapter mAdapter; private boolean animating; public YokoView(@NonNull Context context) { this(context, null); } public YokoView(@NonNull Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public YokoView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public ViewDataBinding initMenuView(@LayoutRes int resId) { removeAllViews(); ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater.from(getContext()), resId, this, true); mView = binding.getRoot(); mView.setClickable(true); sync(); setOnClickListener(v -> { if (!animating) { toggle(); } }); return binding; } public View getMenuView() { return mView; } public void sync() { setVisibility(isCollapsed() ? View.GONE : View.VISIBLE); } public void setApdater(YokoAdapter adapter) { this.mAdapter = adapter; } private void performCallback(int type) { if (mAdapter == null) { return; } if (type == 0) { mAdapter.onCollapsed(this); } if (type == 1) { mAdapter.onExpanded(this); } } public void collapse() { getMenuView().animate() .translationY(-getMenuView().getHeight()) .withStartAction(() -> { animating = true; }) .withEndAction(() -> { animating = false; performCallback(0); setVisibility(View.GONE); }) .start(); } public boolean isCollapsed() { return getMenuView().getTranslationY() <= -getMenuView().getHeight(); } public boolean isExpanded() { return getMenuView().getTranslationY() >= 0; } public void expand(@Nullable Runnable beforeExpand) { getMenuView().animate() .translationY(0) .withStartAction(() -> { animating = true; setVisibility(View.VISIBLE); if (beforeExpand != null) { beforeExpand.run(); } }) .withEndAction(() -> { animating = false; performCallback(1); }) .start(); } public void collapseThenExpand(@Nullable Runnable beforeExpand) { if (isCollapsed()) { expand(beforeExpand); } else { getMenuView().animate() .translationY(-getMenuView().getHeight()) .withStartAction(() -> { animating = true; }) .withEndAction(() -> { animating = false; expand(beforeExpand); }) .start(); } } public void toggle() { if (isCollapsed()) { expand(null); } else { collapse(); } } } 复制代码
使用
- 布局文件
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> <data> <variable name="checkedId" type="androidx.databinding.ObservableInt" /> </data> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".yoko.YokoTestActivity"> <TextView android:id="@+id/textView" android:layout_width="0dp" android:layout_height="0dp" android:layout_marginStart="8dp" android:layout_marginEnd="8dp" android:layout_marginTop="8dp" android:layout_marginBottom="8dp" android:background="@color/colorPrimary" android:gravity="center" android:text="被覆盖的区域" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/toggle" /> <github.hotstu.lib.hof.yokohama.YokoView android:id="@+id/yokoView" android:layout_width="match_parent" android:layout_height="0dp" android:background="#77000000" android:translationZ="1dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="1.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/toggle" app:layout_goneMarginTop="200dp"> </github.hotstu.lib.hof.yokohama.YokoView> <Button android:id="@+id/toggle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:background="@drawable/hof_s_btn_bg" android:onClick="onClick" android:text="栏目0" app:checked="@{checkedId}" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/cte" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:background="@drawable/hof_s_btn_bg" android:onClick="onClick" android:text="栏目1" app:checked="@{checkedId}" app:layout_constraintStart_toEndOf="@+id/toggle" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/cte2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:background="@drawable/hof_s_btn_bg" android:onClick="onClick" android:text="栏目2" app:checked="@{checkedId}" app:layout_constraintStart_toEndOf="@+id/cte" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/cte3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:background="@drawable/hof_s_btn_bg" android:onClick="onClick" android:text="栏目3" app:checked="@{checkedId}" app:layout_constraintStart_toEndOf="@+id/cte2" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout> 复制代码
@Route(path = "/app/yoko", name = "抽屉菜单") public class YokoTestActivity extends AppCompatActivity { private YokoView yokoView; private ObservableInt currentSelect = new ObservableInt(); private ViewDataBinding mBinding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ViewDataBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_yoko_test); binding.setVariable(BR.checkedId, currentSelect); yokoView = findViewById(R.id.yokoView); yokoView.setApdater(new YokoView.YokoAdapter() { @Override public void onCollapsed(YokoView view) { Log.d("hof", "onCollapsed"); } @Override public void onExpanded(YokoView view) { Log.d("hof", "onExpanded"); } }); mBinding = yokoView.initMenuView(R.layout.include_yoko_container_layout); } @BindingAdapter("bind:checked") public static void setChecked(View v, int checkedId) { if (v.getId() == checkedId) { v.setSelected(true); } else { v.setSelected(false); } } public void onClick(View view) { if (currentSelect.get() == view.getId()) { yokoView.toggle(); return; } if (view.getId() == R.id.toggle) { yokoView.collapseThenExpand(() -> { mBinding.setVariable(BR.text, "栏目0内容"); }); } if (view.getId() == R.id.cte) { yokoView.collapseThenExpand(() -> { mBinding.setVariable(BR.text, "栏目1内容"); }); } if (view.getId() == R.id.cte2) { yokoView.collapseThenExpand(() -> { mBinding.setVariable(BR.text, "栏目2内容"); }); } if (view.getId() == R.id.cte3) { yokoView.collapseThenExpand(() -> { mBinding.setVariable(BR.text, "栏目3内容"); }); } currentSelect.set(view.getId()); } } 复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- JS中的数组过滤,从简单筛选到多条件筛选
- 记一次筛选重构
- python素数筛选法浅析
- python如何在列表、字典中筛选数据
- iOS – tableView类型的筛选框实现
- 深度学习在封面图筛选中的应用
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Learn Python the Hard Way
Zed A. Shaw / Addison-Wesley Professional / 2013-10-11 / USD 39.99
Master Python and become a programmer-even if you never thought you could! This breakthrough book and CD can help practically anyone get started in programming. It's called "The Hard Way," but it's re......一起来看看 《Learn Python the Hard Way》 这本书的介绍吧!