可动态格局的Android抽屉之基础[转]

可动态布局的Android抽屉之基础[转]

      以前曾经介绍过《Android提高第十九篇之"多方向"抽屉》,当这个抽屉组件不与周围组件发生压挤的情况下(周围组件布局不变),是比较好使的,但是如果需要对周围组件挤压,则用起来欠缺美观了。

       如下图。在对周围压挤的情况下,抽屉是先把周围的组件一次性压挤,再通过动画效果展开/收缩的,这种做法的好处是快速简单,坏处是如果挤压范围过大,则效果生硬。

可动态格局的Android抽屉之基础[转]

 

      本文实现的自定义抽屉组件,主要针对这种压挤效果做出改良,渐进式压挤周围组件,使得过渡效果更加美观。如下图。

可动态格局的Android抽屉之基础[转]

 

 

 

 

 

 

本文实现的抽屉原理是酱紫:

1.抽屉组件主要在屏幕不可视区域,手柄在屏幕边缘的可视区域。即 抽屉.rightMargin=-XXX + 手柄.width

2.指定一个周围组件为可压挤,即LayoutParams.weight=1;当然用户也可以指定多个View.

3.使用AsyncTask来实现弹出/收缩的动画,弹出:抽屉.rightMargin+=XX,收缩:抽屉.rightMargin-=XX

总结,本文的自定义抽屉虽然对压挤周围组件有过渡效果,但是比较耗资源,读者可以针对不同的情况考虑使用。

本文的源码可以到http://download.csdn.net/detail/hellogv/3615686 下载。

接下来贴出本文全部源代码:

main.xml的源码:

 

<span style="font-family:Comic Sans MS;font-size:18px;"><?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:layout_width="fill_parent" android:layout_height="fill_parent"  
    android:id="@+id/container">  
    <GridView android:id="@+id/gridview" android:layout_width="fill_parent"  
        android:layout_height="fill_parent" android:numColumns="auto_fit"  
        android:verticalSpacing="10dp" android:gravity="center"  
        android:columnWidth="50dip" android:horizontalSpacing="10dip" />  
</LinearLayout></span>

 

 

GridView的Item.xml的源码:

 

<span style="font-family:Comic Sans MS;font-size:18px;"><?xml version="1.0" encoding="utf-8"?>  
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:layout_height="wrap_content" android:paddingBottom="4dip"  
    android:layout_width="fill_parent">  
    <ImageView android:layout_height="wrap_content" android:id="@+id/ItemImage"  
        android:layout_width="wrap_content" android:layout_centerHorizontal="true">  
    </ImageView>  
    <TextView android:layout_width="wrap_content"  
        android:layout_below="@+id/ItemImage" android:layout_height="wrap_content"  
        android:text="TextView01" android:layout_centerHorizontal="true"  
        android:id="@+id/ItemText">  
    </TextView>  
</RelativeLayout>  </span>

 

Panel.java是本文核心,抽屉组件的源码,这个抽屉只实现了从右往左的弹出/从左往右的收缩,读者可以根据自己的需要修改源码来改变抽屉动作的方向:

 

public class Panel extends LinearLayout{  
      
    public interface PanelClosedEvent {  
        void onPanelClosed(View panel);  
    }  
      
    public interface PanelOpenedEvent {  
        void onPanelOpened(View panel);  
    }  
    /**Handle的宽度,与Panel等高*/  
    private final static int HANDLE_WIDTH=30;  
    /**每次自动展开/收缩的范围*/  
    private final static int MOVE_WIDTH=20;  
    private Button btnHandle;  
    private LinearLayout panelContainer;  
    private int mRightMargin=0;  
    private Context mContext;  
    private PanelClosedEvent panelClosedEvent=null;  
    private PanelOpenedEvent panelOpenedEvent=null;  
  
    /** 
     * otherView自动布局以适应Panel展开/收缩的空间变化 
     * @author GV 
     * 
     */   
    public Panel(Context context,View otherView,int width,int height) {  
        super(context);  
        this.mContext=context;  
      
        //改变Panel附近组件的属性  
        LayoutParams otherLP=(LayoutParams) otherView.getLayoutParams();  
        otherLP.weight=1;//支持压挤  
        otherView.setLayoutParams(otherLP);  
          
        //设置Panel本身的属性  
        LayoutParams lp=new LayoutParams(width, height);  
        lp.rightMargin=-lp.width+HANDLE_WIDTH;//Panel的Container在屏幕不可视区域,Handle在可视区域  
        mRightMargin=Math.abs(lp.rightMargin);  
        this.setLayoutParams(lp);  
        this.setOrientation(LinearLayout.HORIZONTAL);  
          
        //设置Handle的属性  
        btnHandle=new Button(context);  
        btnHandle.setLayoutParams(new LayoutParams(HANDLE_WIDTH,height));  
        btnHandle.setOnClickListener(new OnClickListener(){  
  
            @Override  
            public void onClick(View arg0) {  
                LayoutParams lp = (LayoutParams) Panel.this.getLayoutParams();  
                if (lp.rightMargin < 0)// CLOSE的状态  
                    new AsynMove().execute(new Integer[] { MOVE_WIDTH });// 正数展开  
                else if (lp.rightMargin >= 0)// OPEN的状态  
                    new AsynMove().execute(new Integer[] { -MOVE_WIDTH });// 负数收缩  
            }  
              
        });  
        //btnHandle.setOnTouchListener(HandleTouchEvent);  
        this.addView(btnHandle);  
          
        //设置Container的属性  
        panelContainer=new LinearLayout(context);  
        panelContainer.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,  
                LayoutParams.FILL_PARENT));  
        this.addView(panelContainer);  
    }  
  
    /** 
     * 定义收缩时的回调函数 
     * @param event 
     */  
    public void setPanelClosedEvent(PanelClosedEvent event)  
    {  
        this.panelClosedEvent=event;  
    }  
      
    /** 
     * 定义展开时的回调函数 
     * @param event 
     */  
    public void setPanelOpenedEvent(PanelOpenedEvent event)  
    {  
        this.panelOpenedEvent=event;  
    }  
      
    /** 
     * 把View放在Panel的Container 
     * @param v 
     */  
    public void fillPanelContainer(View v)  
    {  
        panelContainer.addView(v);  
    }  
      
    /** 
     * 异步移动Panel 
     * @author hellogv  
     */  
    class AsynMove extends AsyncTask<Integer, Integer, Void> {  
  
        @Override  
        protected Void doInBackground(Integer... params) {  
            int times;  
            if (mRightMargin % Math.abs(params[0]) == 0)// 整除  
                times = mRightMargin / Math.abs(params[0]);  
            else  
                // 有余数  
                times = mRightMargin / Math.abs(params[0]) + 1;  
  
            for (int i = 0; i < times; i++) {  
                publishProgress(params);  
                try {  
                    Thread.sleep(Math.abs(params[0]));  
                } catch (InterruptedException e) {  
                    // TODO Auto-generated catch block  
                    e.printStackTrace();  
                }  
            }  
            return null;  
        }  
  
        @Override  
        protected void onProgressUpdate(Integer... params) {  
            LayoutParams lp = (LayoutParams) Panel.this.getLayoutParams();  
            if (params[0] < 0)  
                lp.rightMargin = Math.max(lp.rightMargin + params[0],  
                        (-mRightMargin));  
            else  
                lp.rightMargin = Math.min(lp.rightMargin + params[0], 0);  
  
            if(lp.rightMargin==0 && panelOpenedEvent!=null){//展开之后  
                panelOpenedEvent.onPanelOpened(Panel.this);//调用OPEN回调函数  
            }  
            else if(lp.rightMargin==-(mRightMargin) && panelClosedEvent!=null){//收缩之后  
                panelClosedEvent.onPanelClosed(Panel.this);//调用CLOSE回调函数  
            }  
            Panel.this.setLayoutParams(lp);  
        }  
    }  
  
}

 

main.java是主控部分,演示了Panel的使用:

 

public class main extends Activity {  
    public Panel panel;  
    public LinearLayout container;  
    public GridView gridview;  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
        this.setTitle("“可动态布局”的抽屉组件之构建基础-----hellogv");  
        gridview = (GridView) findViewById(R.id.gridview);  
        container=(LinearLayout)findViewById(R.id.container);  
        panel=new Panel(this,gridview,200,LayoutParams.FILL_PARENT);  
        container.addView(panel);//加入Panel控件  
          
        //新建测试组件  
        TextView tvTest=new TextView(this);  
        tvTest.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT));  
        tvTest.setText("测试组件,红字白底");  
        tvTest.setTextColor(Color.RED);  
        tvTest.setBackgroundColor(Color.WHITE);  
        //加入到Panel里面  
        panel.fillPanelContainer(tvTest);  
          
        panel.setPanelClosedEvent(panelClosedEvent);  
        panel.setPanelOpenedEvent(panelOpenedEvent);  
          
        //往GridView填充测试数据  
        ArrayList<HashMap<String, Object>> lstImageItem = new ArrayList<HashMap<String, Object>>();  
        for (int i = 0; i < 100; i++) {  
            HashMap<String, Object> map = new HashMap<String, Object>();  
            map.put("ItemImage", R.drawable.icon);  
            map.put("ItemText", "NO." + String.valueOf(i));  
            lstImageItem.add(map);  
        }  
  
        SimpleAdapter saImageItems = new SimpleAdapter(this,   
                lstImageItem,  
                R.layout.item,   
                new String[] { "ItemImage", "ItemText" },  
                new int[] { R.id.ItemImage, R.id.ItemText });  
        gridview.setAdapter(saImageItems);  
        gridview.setOnItemClickListener(new ItemClickListener());  
          
    }  
  
    PanelClosedEvent panelClosedEvent =new PanelClosedEvent(){  
  
        @Override  
        public void onPanelClosed(View panel) {  
            Log.e("panelClosedEvent","panelClosedEvent");  
        }  
          
    };  
      
    PanelOpenedEvent panelOpenedEvent =new PanelOpenedEvent(){  
  
        @Override  
        public void onPanelOpened(View panel) {  
            Log.e("panelOpenedEvent","panelOpenedEvent");  
        }  
          
    };  
      
    class ItemClickListener implements OnItemClickListener {  
        @Override  
        public void onItemClick(AdapterView<?> arg0,View arg1, int arg2, long arg3) {  
            @SuppressWarnings("unchecked")  
            HashMap<String, Object> item = (HashMap<String, Object>) arg0  
                    .getItemAtPosition(arg2);  
            setTitle((String) item.get("ItemText"));  
        }  
  
    }

 

本文来自http://blog.csdn.net/hellogv/article/details/6789698