自定义菜单收起展开动画(二)
来源:互联网 发布:libcurl https linux 编辑:程序博客网 时间:2024/06/04 20:04
今天周一,新的一周,水的开始。
上次发完博客后,自己在项目中使用不是特别的方便,除了要写很多布局之外,还要写诸多的点击事件的监听,不是非常的方便,于是花了一天的时间封装了一下。
首先我们先缕缕思路,首先我想实现一个自定义的控件可以实现全部的父子菜单的功能,所以在原有的adapter基础上,我们需要增加累死expanedlistview类似的adapter的实现。
public abstract class SettingsAdapter<T> { private Context context; private List<T> mParentList; private List<List<T>> mChildList; public SettingsAdapter(Context context, List<T> mParentList, List<List<T>> mChildList) { this.context = context; this.mParentList = mParentList; this.mChildList = mChildList; } public int getParentCount(){ return mParentList==null?0:mParentList.size(); } public T getItem(int position){ return mParentList.get(position); } public int getChildCount(int parentPosition){ return mChildList==null?0: mChildList.get(parentPosition).size(); } public T getChildItem(int parentPosition,int childPosition){ return mChildList.get(parentPosition).get(childPosition); } public abstract View getParentView(View view,int position); public abstract View getChildView(View view,int parentPosition,int childPosition); public interface onDataChanged { void changed(); } public void setOnDataChanged(onDataChanged onDataChanged) { this.onDataChanged = onDataChanged; } public onDataChanged onDataChanged; public void notifyDataSetChanged(){ onDataChanged.changed(); }}这次的adapter和上次的adapter大致上是一样的,不过是多了父菜单的view,以便于我们对于父布局的定制。
然后我们在原来的SettingView上进行修改,这次我们因为要实现的是一整个的菜单,所以依然是继承linearlayout,在初始化中设置方向为竖向。
public SettingsView(Context context) { this(context, null); } public SettingsView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SettingsView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.context=context; setOrientation(VERTICAL); }然后我们在SettingView中实现setAdapter这个接口,和上个博客的写法大致一样。
public void setAdapter(SettingsAdapter adapter){ this.adapter=adapter; changeAdapter(); }下面我们重点看一下changAdapter里面的方法。
private void changeAdapter() { removeAllViews(); SettingsAdapter settingsAdapter=this.adapter; for(int i=0;i<settingsAdapter.getParentCount();i++){ final View parentView = settingsAdapter.getParentView(this, i); parentView.setTag(i); addView(parentView); parentView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if(onItemClickListener!=null){ onItemClickListener.ParentClicked((Integer) parentView.getTag()); } } }); innerLayout=new LinearLayout(context); LinearLayout.LayoutParams lp=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); innerLayout.setLayoutParams(lp); innerLayout.setOrientation(LinearLayout.VERTICAL); for(int j=0;j<settingsAdapter.getChildCount(i);j++){ final View childView = settingsAdapter.getChildView(this, i, j); childView.setTag(String.valueOf(i) + String.valueOf(j)); childView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if(onItemClickListener!=null){ String tag= (String) childView.getTag(); onItemClickListener.childClicked(TagToInt(tag,0,1),TagToInt(tag,1,2)); } } }); innerLayout.addView(childView); } addView(innerLayout); } }首先,我们先通过removeAllViews来清空布局,以免发生什么可怕的事情,然后通过循环adapter中的getparentCount方法来加载总共有几项父菜单,然后我们通过innerLinearLayout来add子菜单的布局,方便接下来我们对子菜单的高度的计算。然后我们通过对parentView和childView设置tag来实现点击事件,其中的tagToInt的方法在这里。
private int TagToInt(String str,int start,int end){ String substring = str.substring(start, end); int i= Integer.parseInt(substring); return i; }我们在adapter中通过接口onDataChanged 实现了notify的功能,所以我们需要在SettingView中对它进行实现,所以在settingView中实现该接口,并完成回调。
@Override public void changed() { changeAdapter(); }以及我们对click事件的回调。
public OnItemClickListener onItemClickListener; public void setOnItemClickListener(OnItemClickListener onItemClickListener) { this.onItemClickListener = onItemClickListener; } public interface OnItemClickListener{ void ParentClicked(int position); void childClicked(int parentPosition,int childPosition); }
这样的话我们的控件已经有点样子了,但是不满足是程序员进步的最大动力,所以我继续封装了对于控件高度的计算。
public List<View> getChildLayout(){ List<View> list=new ArrayList<>(); for(int i=1;i<getChildCount();i+=2){ View childView = getChildAt(i); list.add(childView); } return list; }这里的代码会有点生涩,首先我们通过循环子控件的个数,第0个是父菜单的parentView,第1个才是我们所需要的子菜单的innerlayout,所以我们从1开始循环,i+=2,也就非常容易的就懂了,因为我们的子菜单的view是每隔一行parentView的,所以需要i+=2。这样我们就得了子菜单的所有view集合。接下来我们就开始计算高度,上文中,我们提到了在activity中和fragment中计算高度的方法是不一样的,所以我们在这里定义一个boolean值来判断是否在activity中。
public boolean isActivity=true;然后在activity中通过post方法来获取子菜单View的高度。
this.post(new Runnable() { @Override public void run() { List<View> childLayout = getChildLayout(); heightList.clear(); for(int i=0;i<childLayout.size();i++){ int height = childLayout.get(i).getHeight(); heightList.add(height); } } });然后再fragment中就稍微复杂一些,首先我们要实现 ViewTreeObserver.OnGlobalLayoutListener这个监听来监听控件的高度。
ViewTreeObserver.OnGlobalLayoutListener listener=new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { List<View> childLayout = getChildLayout(); heightList.clear(); for(int i=0;i<childLayout.size();i++){ int height = childLayout.get(i).getHeight(); heightList.add(height); } } };这样我们不管在activity中还是fragment中都获取了控件的高度,于是我们继续封装出来一个方法来判断是否在activity中。
public void initHeight(boolean isActivity){ this.isActivity=isActivity; if(isActivity){ this.post(new Runnable() { @Override public void run() { List<View> childLayout = getChildLayout(); heightList.clear(); for(int i=0;i<childLayout.size();i++){ int height = childLayout.get(i).getHeight(); heightList.add(height); } } }); }else{ this.getViewTreeObserver().addOnGlobalLayoutListener(listener); } }于是我们在每次调用的时候只需要在findview后面initHeight方法来获取子菜单view的高度集合了。值得注意的是因为ViewTreeObserver.OnGlobalLayoutListener是适时监听的,所以我们需要在首次点击之后动画效果生成之前移除他的监听。
public ViewTreeObserver.OnGlobalLayoutListener getListener(){ return listener; } //移除监听 public void removeOnGlobalLayoutListener(View v, ViewTreeObserver.OnGlobalLayoutListener listener) { if (Build.VERSION.SDK_INT < 16) { v.getViewTreeObserver().removeGlobalOnLayoutListener(listener); } else { v.getViewTreeObserver().removeOnGlobalLayoutListener(listener); } }这样我们就基本上完成了对控件的封装,最后我们在对上篇博客中的动画效果做最后一层的封装。
<pre name="code" class="java"> public void animateBegin(int position){ if (getChildLayout().get(position).getVisibility() == View.VISIBLE) { if(!isActivity){ removeOnGlobalLayoutListener(getChildView(position), getListener()); } AnimUtils.animatorClose(getChildLayout().get(position), getChildItemHeight(position)); } else { AnimUtils.animatorOpen(getChildLayout().get(position), getChildItemHeight(position)); } }这里的封装也十分简单,首先就是判断是否在fragment中,如果是fragment我们就移除掉对于控件高度的监听,是不是非常的通俗易懂呢?最后我们来实践一下。
<pre name="code" class="html"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <ScrollView android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <com.example.mydemo2.SettingsView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/sv" ></com.example.mydemo2.SettingsView> <Button android:layout_width="match_parent" android:layout_height="45dp" android:id="@+id/change_fragment" android:text="切换到fragment" /> <FrameLayout android:layout_width="match_parent" android:layout_height="500dp" android:id="@+id/fragment_layout" ></FrameLayout> </LinearLayout> </ScrollView></RelativeLayout>
这是MainActivity的布局文件,我们点击button之后会调用repalce fragment的方法,来显示fragment中的settingview。
sv= (SettingsView) findViewById(R.id.sv); btn= (Button) findViewById(R.id.change_fragment); sv.initHeight(true); for(int i=0;i<3;i++){ mList.add(i+"这是父菜单"); } for(int i=0;i<3;i++){ List<String> list=new ArrayList<>(); for(int j=0;j<5;j++){ list.add("这是第"+i+"父菜单,第+"+j+"子菜单"); } mList2.add(list); } SettingsAdapter adapter=new SettingsAdapter(MainActivity.this,mList,mList2) { @Override public View getParentView(View view, int position) { View view1 = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_a, null); TextView tv= (TextView) view1.findViewById(R.id.tv1); tv.setText(mList.get(position)); return view1; } @Override public View getChildView(View view, int parentPosition, int childPosition) { View view2 = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_b, null); TextView tv= (TextView) view2.findViewById(R.id.tv2); tv.setText(mList2.get(parentPosition).get(childPosition)); return view2; } }; sv.setAdapter(adapter); sv.setOnItemClickListener(new SettingsView.OnItemClickListener() { @Override public void ParentClicked(int position) { sv.animateBegin(position); } @Override public void childClicked(int parentPosition, int childPosition) { Toast.makeText(MainActivity.this, mList2.get(parentPosition).get(childPosition), Toast.LENGTH_SHORT).show(); } }); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); TestFragment fragment=new TestFragment(); ft.replace(R.id.fragment_layout,fragment); ft.commit(); } }); }这是MainActivity中全部的代码,并没有什么非常有难度的,在我们的封装下变得十分的简单并且清晰,即使你以后甩锅不干之后,也能造福下一个程序汪,是不是很好呢?汪汪汪!最后附上效果图一张:
1 0
- 自定义菜单收起展开动画(二)
- 自定义菜单收起展开动画
- jquery 菜单展开收起
- 高级动画-圆形树展开、收起动画
- 三级菜单的展开与收起功能
- js实现菜单的收起和展开
- JQuery 向下展开收起动画( slideDown(),slideUp() )
- 自定义view实现TextView展开收起效果
- 导航条(收起展开)
- jQuery学习笔记(3)---点击触发菜单的展开与收起
- 自定义展开菜单
- iOS高级动画:圆形树展开&收起动画
- 菜单展开 合并<二>
- 自定义可展开收起TextView,展开收起按钮紧跟文本内容
- Android中SpannableString学习以及实现自定义TextView的显示更多(展开)和收起功能
- javascript实现一段文字展开、收起(默认收起)
- Wipe In and Wipe Out 抹进、抹出(动画显示、动画隐藏)(展开、折叠)(拉下、收起)
- JQuery 动画实例:下拉列表框展开收起
- 使用PCRE库多次匹配同一个字符串
- 线程生命周期
- tf-idf doc
- C++笔记
- SPSS基础教程:SPSS菜单命令详解(二)
- 自定义菜单收起展开动画(二)
- Linux设备模型(1)_基本概念
- Struts2学习步骤
- iPhone6手写输入法如何设置 方法详解
- leetcode 2. Add Two Numbers
- jQuery 鼠标滑过图片 预览大图(附注释)
- chmod----改变一个或多个文件的存取模式(mode)
- Linux笔记
- cassandra 3.5 win7安装