Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 自定義菜單收起展開動畫

自定義菜單收起展開動畫

編輯:關於Android編程

最近需求sama聯合美工娘娘又改了界面,整體的界面是華麗了不少,但是大神何必為難弱智兒童的我呢,下面先看看新界面~

 

\

 

很經典的菜單設計,不過畢竟是版本更迭,不適合在原有基礎上大修改改,菜單總共分了4個父菜單和若干個子菜單,點擊父菜單會隱藏子菜單,其中還要有收起展開動畫,這個首先令我想起了expanedlistview。說動手就動手,花了幾分鐘先寫個demo測試以下吧。

這個很簡單的布局代碼,不必多說了~

然後定義一個實體類,包含父子菜單所必需的內容

public class Bean {

public Bean(String title, List childBeans) {

this.title = title;

this.childBeans = childBeans;

}

public Bean() {

}

public String getTitle() {

return title;

}

public void setTitle(String title) {

this.title = title;

}

public List getChildBeans() {

return childBeans;

}

public void setChildBeans(List childBeans) {

this.childBeans = childBeans;

}

private String title;

private List childBeans;

public static class childBean{

private String title;

public childBean(String title) {

this.title = title;

}

public String getTitle() {

return title;

}

public void setTitle(String title) {

this.title = title;

}

}

}

這幾行代碼也不必多說,一目了然,如果需要添加其他的 自然也可以自己添加~

最後開始寫自定義adapter了,也很簡單不是嗎?

public class TestAdapter extends BaseExpandableListAdapter {

private List groups;

private Context context;

public TestAdapter( Context context, List groups){

this.context=context;

this.groups=groups;

}

@Override

public int getGroupCount() {

return groups.size();

}

@Override

public int getChildrenCount(int groupPosition) {

return groups.get(groupPosition).getChildBeans().size();

}

@Override

public Object getGroup(int groupPosition) {

return groups.get(groupPosition);

}

@Override

public Object getChild(int groupPosition, int childPosition) {

return groups.get(groupPosition).getChildBeans().get(childPosition);

}

@Override

public long getGroupId(int groupPosition) {

return groupPosition;

}

@Override

public long getChildId(int groupPosition, int childPosition) {

return childPosition;

}

@Override

public boolean hasStableIds() {

return false;

}

@Override

public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {

View view= LayoutInflater.from(context).inflate(R.layout.item_a,null);

TextView tv= (TextView) view.findViewById(R.id.a_text);

tv.setText(groups.get(groupPosition).getTitle());

return view;

}

@Override

public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {

View view= LayoutInflater.from(context).inflate(R.layout.item_a,null);

TextView tv= (TextView) view.findViewById(R.id.a_text);

tv.setText(groups.get(groupPosition).getChildBeans().get(childPosition).getTitle());

return view;

}

@Override

public boolean isChildSelectable(int groupPosition, int childPosition) {

return false;

}

}

很簡單的代碼,畢竟只是demo,大家就將就一下看看,最後在activity裡調用一下,我們來看效果吧~

public class MainActivity extends AppCompatActivity {

private ExpandableListView listView;

private List mList=new ArrayList<>();

private List childList=new ArrayList<>();

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

listView= (ExpandableListView) findViewById(R.id.exlistview);

Bean.childBean childBean;

for(int j=0;j<3;j++){

childBean=new Bean.childBean("aaaa"+j);

childList.add(childBean);

}

for(int i=0;i<3;i++){

mList.add(new Bean(i+"",childList));

}

TestAdapter adapter=new TestAdapter(MainActivity.this,mList);

listView.setAdapter(adapter);

listView.setGroupIndicator(null);

}

}

最後 上動圖看效果:

 

\

 

太粗糙了,而且沒有收起展開的動畫,沒辦法,繼續百度吧,然後找到了一個叫animationExpanedListView的東東,但是我感覺比較久遠了,上次更新已經2年之前了,有想實驗的小伙伴可以去這裡---->https://github.com/idunnololz/AnimatedExpandableListView。

怎麼辦呢,看來只有自己想辦法了。首先屢清楚思路,模仿listview的效果可以試試,所以首先寫一個自定義的adapter出來~

public abstract class SettingsAdapter {

private Context context;

private List mList;

public SettingsAdapter(Context context, List mList){

this.context=context;

this.mList=mList;

}

public SettingsAdapter(T[] mDatas){

mList=new ArrayList(Arrays.asList(mDatas));

}

public int getCount(){

return mList==null?0:mList.size();

}

public T getItem(int position){

return mList.get(position);

}

public void notifyDataSetChanged(){

onDataChanged.changed();

}

public abstract View getView(SettingView parent, int position);

public interface onDataChanged {

void changed();

}

public void setOnDataChanged(onDataChanged onDataChanged) {

this.onDataChanged = onDataChanged;

}

public onDataChanged onDataChanged;

}

這一段代碼很容易理解,跟普通的adapter沒有啥不一樣的~繼續向下我們要實現子菜單的布局,首先我們繼承linearlayout,因為菜單畢竟是自上向下的嘛~

public class SettingView extends LinearLayout implements SettingsAdapter.onDataChanged{

private Context context;

private SettingsAdapter adapter;

public SettingView(Context context) {

this(context, null);

}

public SettingView(Context context, AttributeSet attrs) {

this(context, attrs, 0);

}

public SettingView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

this.context=context;

setOrientation(VERTICAL);

}

接下來我們公開一個adapter調用的方法用法調用adapter;

public void setAdapter(SettingsAdapter adapter){

this.adapter=adapter;

changeAdapter();

}

這依然是很簡單的代碼。我們接下來看changeAdapter裡面的內容

private void changeAdapter() {

removeAllViews();

SettingsAdapter settingsAdapter = this.adapter;

for(int i=0;i

一目了然是不是,如果看不懂沒關系,那就多看幾遍,如果多看幾遍還是看不懂,也沒關系,其實我也不懂。

最後我們通過接口回調將接口公布出去,這是我們的接口以及實現的adapter中接口:

public interface OnItemClick {

void click(View v,int i);

}

public void setOnItemClick(OnItemClick onItemClick) {

this.onItemClick = onItemClick;

}

private OnItemClick onItemClick;

@Override

public void changed() {

changeAdapter();

}

最後在xml中的布局就是這樣子的:

Textview暫時充當的是父菜單,settingview就是子菜單咯~

tv= (TextView) findViewById(R.id.tv1);

sv= (SettingView) findViewById(R.id.test_layout);

for(int i=0;i<5;i++){

mList.add(i+"");

}

SettingsAdapter adapter=new SettingsAdapter(MainActivity.this,mList) {

@Override

public View getView(SettingView parent, int position) {

View view=getLayoutInflater().inflate(R.layout.item_a,null);

TextView tv= (TextView) view.findViewById(R.id.a_text);

tv.setText(mList.get(position));

return view;

}

};

sv.setAdapter(adapter);

sv.setOnItemClick(new SettingView.OnItemClick() {

@Override

public void click(View v,int i) {

Toast.makeText(MainActivity.this,mList.get(i),Toast.LENGTH_SHORT).show();

}

});

}

最後在activity裡敲這些很簡單的代碼,差不多第一步的自定義布局就算實現了。看效果

\

 

布局是寫好了,但是暫時還沒有任何的點擊效果和動畫效果,這時候想到了什麼呢,屬性動畫,當當當~不過objectAnimator裡並沒有對View高度的動畫,不過我們知道valueAnimator是可以實現這個的,不多說看代碼~

public static ValueAnimator DropAnim(final View view,int start,int end){

ValueAnimator animator=ValueAnimator.ofInt(start,end);

animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

int value= (Integer) animation.getAnimatedValue();

ViewGroup.LayoutParams lp = view.getLayoutParams();

lp.height=value;

view.setLayoutParams(lp);

}

});

return animator;

}

首先,我們需要把target的view 和view的起始高度傳起來,最後通過動畫的listener實現對view高度的動態變化,看到這裡是不是有些思路了~~~

public static void animatorOpen(final View view, int height){

view.setVisibility(View.VISIBLE);

AnimatorSet set=new AnimatorSet();

set.setDuration(1000);

ValueAnimator animator = DropAnim(view, 0, height);

ObjectAnimator oa=ObjectAnimator.ofFloat(view, View.ALPHA, 0.0f, 1.0f);

set.playTogether(animator,oa);

set.setDuration(1000);

set.start();

}

public static void animatorClose(final View view, int height){

AnimatorSet set=new AnimatorSet();

set.setDuration(1000);

ValueAnimator animator = DropAnim(view, height,0);

ObjectAnimator oa=ObjectAnimator.ofFloat(view,View.ALPHA,1.0f,0.0f);

set.playTogether(animator,oa);

set.addListener(new AnimatorListenerAdapter() {

@Override

public void onAnimationEnd(Animator animation) {

super.onAnimationEnd(animation);

view.setVisibility(View.GONE);

}

});

set.start();

}

這是動畫開關的兩個方法,方法大致相同,我們不僅實現了高度的變化,而且加入了透明度的變化,當然你也可以加入一些其他的動畫,這裡就不多做介紹了。

最後在在點擊事件中通過判斷子菜單的vissable來判斷動畫的開合,是不是很簡單呢?最後的一個難點是獲取view的高度,我們並沒有在自定義的布局裡直接獲取到他的高度,當然你也可以在自定義view的時候直接通過 getMeasuredHeight() 的方法獲得出來,當然也可以通過post方法進行獲取

sv.post(new Runnable() {

@Override

public void run() {

height=sv.getHeight();

}

});

tv.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

if(sv.getVisibility()==View.VISIBLE){

AnimUtils.animatorClose(sv,height);

}else{

AnimUtils.animatorOpen(sv,height);

}

}

});

最後大功告成~來看一下動畫效果吧~

\

 

是不是很炫呢?最後有一個小問題,就是如何在fragment中獲取控件的高度呢,起初我也是傻傻的post方法,然並卵的是然並卵啊~最後還是找到了答案~通過

ViewTreeObserver來獲取,然後在點擊或者初始化完成之後進行移除監聽,不然他會一直監聽控件的高度,切記,移除這個監聽,必須放在動畫之前,否則會出現意想不到的彩蛋呢~

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved