鱼鱼Chen之学写自己的apk(二) 使用SlidingPaneLayout实现漂亮的布局

首先,这是原代码的链接:http://www.eoeandroid.com/thread-545123-1-1.html

可惜的是,当初原作者只上了代码,并没有作解释。一开始理解的时候的确有点费劲。于是这篇算作是译文吧,基本是自己的理解,所以也许有点出入,当然了,也加入了ActionBar的部分知识点。

一、分析一下结构


1个Activity以及它的2个Fragment,布局是对应的,还有一个自定义的ActionProvider,background_gradient是一个自定义的<shape>

关于Fragment的完全解析:http://blog.csdn.net/lmj623565791/article/details/37970961


二、原理分析

其实呢,原理并不复杂。就是将两个Fragment放在Activity里,通过SlidingPaneLayout这个组件(在v4包里),实现监听滑动达到从右Fragment完全打开到左Fragment完全打开的过程。当中实现动态地改变外边距(Margin)


三、新建两个Fragment

以左侧(菜单)Fragment为例

public class MenuFragment extends Fragment {
	private View fragmentView;

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		fragmentView = inflater.inflate(R.layout.slidingpane_menu, container,
				false);
		return fragmentView;
	}

	public View getFragmentView() {
		return fragmentView;
	}

}
这边还是很好理解的,只要用LayoutInflater类的inflate方法把不觉导入,为Fragmemnt创建自己的布局即可。

getFragment方法在接下来的MainActivity中会用到,先放着。

右侧(内容)Fragment差不多

public class ContentFragment extends Fragment {
	private View fragmentView;

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		fragmentView = inflater.inflate(R.layout.slidingpane_content,
				container, false);
		return fragmentView;
	}

	public FrameLayout.LayoutParams getCurrentParams() {
		return (LayoutParams) fragmentView.getLayoutParams();
	}

	public void setCurrentParams(FrameLayout.LayoutParams params) {
		fragmentView.setLayoutParams(params);
	}
}
自定义一组get、set方法同样稍后解释

两个布局文件,首先是左侧Fragment的

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginTop="30dp" >

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/io" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="   小精灵"
            android:textColor="@android:color/white"
            android:textSize="20sp" />
    </LinearLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="3dp"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:background="@android:color/white" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="15dp"
        android:layout_marginRight="15dp"
        android:layout_weight="1"
        android:orientation="vertical" >

        <Button
            android:id="@+id/btn01"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@null"
            android:drawableLeft="@drawable/icon01"
            android:gravity="left|center"
            android:text="松饼人"
            android:textColor="@android:color/white"
            android:textSize="20sp" />

        <Button
            android:id="@+id/btn02"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@null"
            android:drawableLeft="@drawable/icon02"
            android:gravity="left|center"
            android:text="圣诞树"
            android:textColor="@android:color/white"
            android:textSize="20sp" />

        <Button
            android:id="@+id/btn03"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@null"
            android:drawableLeft="@drawable/icon03"
            android:gravity="left|center"
            android:text="雪人"
            android:textColor="@android:color/white"
            android:textSize="20sp" />

        <Button
            android:id="@+id/btn04"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@null"
            android:drawableLeft="@drawable/icon04"
            android:gravity="left|center"
            android:text="礼物"
            android:textColor="@android:color/white"
            android:textSize="20sp" />

        <Button
            android:id="@+id/btn05"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@null"
            android:drawableLeft="@drawable/icon05"
            android:gravity="left|center"
            android:text="糖果"
            android:textColor="@android:color/white"
            android:textSize="20sp" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="@null"
            android:drawableLeft="@drawable/icon_mini01"
            android:gravity="left|center"
            android:text="铲子"
            android:textColor="@android:color/darker_gray" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="@null"
            android:drawableLeft="@drawable/icon_mini02"
            android:gravity="left|center"
            android:text="斧头"
            android:textColor="@android:color/darker_gray" />
    </LinearLayout>

</LinearLayout>
接着是右侧Fragment的

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@android:color/black" >
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="这是内容部分"
        android:layout_gravity="center_horizontal"
        android:textColor="@android:color/white"
        android:textSize="25sp"/>

</LinearLayout>
这里有一个注意点哦!!右侧Fragment布局的背景色设为任意颜色,而左侧Fragment布局的颜色设为透明。为什么呢?我就不多解释,稍微改一下,跑跑看就知道喽~!

四、完成Activity的布局文件

先上代码,再一点点分析

<FrameLayout 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"
    tools:context="com.dota.example.fishychenofslidingpanelayout.MainActivity" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1" >

            <View
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@drawable/background_darksider" />

            <View
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@drawable/background_gradient" />
        </FrameLayout>

        <View
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="3"
            android:background="@android:color/black" />
    </LinearLayout>

    <android.support.v4.widget.SlidingPaneLayout
        android:id="@+id/slidingPaneLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/transparent" >

        <FrameLayout
            android:id="@+id/menuLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginRight="100dp" />
        <FrameLayout
            android:id="@+id/contentLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </android.support.v4.widget.SlidingPaneLayout>

</FrameLayout>
关于结构呢,我给个更清晰的图
这边要用到帧布局(FrameLayout)

关于FrameLayout,这是个最简单的布局,所有的控件都会自动排到左上角,可能你会问了,这有什么用呢?首先,它很简单,吃的系统资源很少。其次,很很好的满足我们的需求,就像是ps里的图层一样。

好了,接着我们分析布局原理。我们定义一个内嵌的LinearLayout,在内部再写一个FrameLayotu和一个空View,高度比为1:3。空View填充黑色。子FrameLayout里填两个空View,一个背景是图片,一个是渐变背景,用来覆盖在前者上。关于<shape>,如下所示,用来做一个自定义形状的背景。

几个属性简单说一下:<corners>用来生成圆角矩形

<padding>用来设置内边距

<size>用来设置尺寸

<solid>填充色

<stroke>边框线

<gradient>渐变色

我这边只用到了最后一条,分别设置起始色,结束色,以及变化角度(270是自上而下)

<shape xmlns:android="http://schemas.android.com/apk/res/android" >
    <gradient
        android:startColor="@android:color/transparent"
        android:endColor="@android:color/black"
        android:angle="270"/>
   
</shape>
背景的效果如下

接着把SlidingPaneLayout放上去(FrameLayout里后来的会覆盖在先写的),里面再放两个帧布局。还记得之前说的吗?实现的是右侧Fragment完整显示到左侧Fragment完整显示。所以,这边给左边的布局一个MarginRight属性


五、主Activity

这边就是写关于变换的地方了

先实例化一个DisplayMetrics对象,接着我们要用这个来获取一些屏幕的显示信息

private DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
这样,我们就可以用metrics的一些field来获得屏幕的一些显示信息了


完整地看一下,声明的变量

private DisplayMetrics metrics = new DisplayMetrics();
	private SlidingPaneLayout slidingPaneLayout;
	private MenuFragment menuFragment;
	private ContentFragment contentFragment;
	private int maxMargin = 0;
	private ActionBar actionBar;

oncreate()方法
@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		initActionBar();
		initPaneLayout();
		slidingPaneLayout.setPanelSlideListener(new PanelSlideListener() {

			@Override
			public void onPanelSlide(View arg0, float percent) {
				int contentMargin = (int) (percent * maxMargin);
				FrameLayout.LayoutParams params = contentFragment
						.getCurrentParams();
				params.setMargins(0, 0, 0, contentMargin);
				contentFragment.setCurrentParams(params);

				float scale = 1 - ((1 - percent) * maxMargin * 4)
						/ (float) metrics.heightPixels;
				menuFragment.getFragmentView().setScaleX(scale);
				menuFragment.getFragmentView().setScaleY(scale);
				menuFragment.getFragmentView().setPivotX(0);
				menuFragment.getFragmentView().setPivotY(
						metrics.heightPixels / 2);
				menuFragment.getFragmentView().setAlpha(percent);
			}

			@Override
			public void onPanelOpened(View arg0) {
				// TODO Auto-generated method stub

			}

			@Override
			public void onPanelClosed(View arg0) {
				// TODO Auto-generated method stub

			}
		});
	}
好,我们一点点看。

initActionBar()方法我就不讲了。里面是我自己写的关于actionbar的一些操作,可以参考我写的关于ActionBar的学习        http://blog.csdn.net/zerolovesc1993/article/details/43338675

接着我们看initPaneLayout()方法,用来初始化SlidingPaneLayout的

private void initPaneLayout() {
		getWindowManager().getDefaultDisplay().getMetrics(metrics);
		slidingPaneLayout = (SlidingPaneLayout) findViewById(R.id.slidingPaneLayout);
		menuFragment = new MenuFragment();
		contentFragment = new ContentFragment();
		FragmentTransaction transaction = getFragmentManager()
				.beginTransaction();
		transaction.replace(R.id.menuLayout, menuFragment);
		transaction.replace(R.id.contentLayout, contentFragment);
		transaction.commit();
		maxMargin = metrics.heightPixels / 5;
	}
关于Fragment的话,用一个FragmentTransaction对象的replace方法,来将activity_main中的控件和Fragment绑起来

完成之后用commit方法提交。当然,再此之前需要得到一个FragmentManager类的对象来开始事务(beginTransaction)。这边的maxMargin定的是屏幕高的5分之1,有什么用,我们接下来说。

我们基本快完成了,为我们的slidingpaneLayout组件设置对应的滑动监听器

setPanelSlideListener

完成3个抽象方法,我们只要写滑动监听的那个

@Override
			public void onPanelSlide(View arg0, float percent) {
				
			}
float  percent对应的那个是啥呢?是一个0~1的浮点型,用来模拟一个滑动进度条一样(可以这么理解,所以我把变量名改成了percent)
<span style="white-space:pre">				</span>int contentMargin = (int) (percent * maxMargin);
				FrameLayout.LayoutParams params = contentFragment
						.getCurrentParams();
				params.setMargins(0, 0, 0, contentMargin);
				contentFragment.setCurrentParams(params);
这一段用来设置右边的动态变化的。原理很简单,先得到右侧的LayoutParams,这个东西相当于一个layout的信息包,里面封装了关于layout的信息。再重置Margin,四个参数对应左、上、右、下,这边的contentMargin就是我们根据前边定的maxMargin结合percent来实现动态根据滑动距离改变右侧的下边距。


接着是左边的动态变化的

float scale = 1 - ((1 - percent) * maxMargin * 4)
						/ (float) metrics.heightPixels;
				menuFragment.getFragmentView().setScaleX(scale);
				menuFragment.getFragmentView().setScaleY(scale);
				menuFragment.getFragmentView().setPivotX(0);
				menuFragment.getFragmentView().setPivotY(
						metrics.heightPixels / 2);
				menuFragment.getFragmentView().setAlpha(percent);
用到了三个方法,setScale用来设置变换的  1代表不变,setPivot  用来设置变换的起点的(从左侧的当中开始),setAlpha用来设置透明度,float   0为不可见,1为100%显示。X,Y分别对象变换的坐标,至于scale,是自己改的,要是嫌麻烦,可以直接用percent,不过效果没这个好。(一开始的时候,就有一个不错的大小,用percent的话,一开始的大小太小了)

Activity完整的代码

public class MainActivity extends Activity {
	private DisplayMetrics metrics = new DisplayMetrics();
	private SlidingPaneLayout slidingPaneLayout;
	private MenuFragment menuFragment;
	private ContentFragment contentFragment;
	private int maxMargin = 0;
	private ActionBar actionBar;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		initActionBar();
		initPaneLayout();
		slidingPaneLayout.setPanelSlideListener(new PanelSlideListener() {

			@Override
			public void onPanelSlide(View arg0, float percent) {
				int contentMargin = (int) (percent * maxMargin);
				FrameLayout.LayoutParams params = contentFragment
						.getCurrentParams();
				params.setMargins(0, 0, 0, contentMargin);
				contentFragment.setCurrentParams(params);

				float scale = 1 - ((1 - percent) * maxMargin * 4)
						/ (float) metrics.heightPixels;
				menuFragment.getFragmentView().setScaleX(scale);
				menuFragment.getFragmentView().setScaleY(scale);
				menuFragment.getFragmentView().setPivotX(0);
				menuFragment.getFragmentView().setPivotY(
						metrics.heightPixels / 2);
				menuFragment.getFragmentView().setAlpha(percent);
			}

			@Override
			public void onPanelOpened(View arg0) {
				// TODO Auto-generated method stub

			}

			@Override
			public void onPanelClosed(View arg0) {
				// TODO Auto-generated method stub

			}
		});
	}

	private void initPaneLayout() {
		getWindowManager().getDefaultDisplay().getMetrics(metrics);
		slidingPaneLayout = (SlidingPaneLayout) findViewById(R.id.slidingPaneLayout);
		menuFragment = new MenuFragment();
		contentFragment = new ContentFragment();
		FragmentTransaction transaction = getFragmentManager()
				.beginTransaction();
		transaction.replace(R.id.menuLayout, menuFragment);
		transaction.replace(R.id.contentLayout, contentFragment);
		transaction.commit();
		maxMargin = metrics.heightPixels / 5;
	}

	private void initActionBar() {
		actionBar = getActionBar();
		actionBar.setHomeButtonEnabled(true);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.menu_actionbar, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case android.R.id.home:
			slidingPaneLayout.openPane();
			break;
		case R.id.menu_candy:
			slidingPaneLayout.closePane();
			break;
		default:
			break;
		}
		return true;
	}
}
其中写到的openPane和closePane方法是对应的,用来打开左侧Fragment和关闭。其中,要用到actionBar.setHomeButtonEnabled(true)方法才能让home按钮可以被点击


百度云链接:

http://pan.baidu.com/s/1hqIPC6S






  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值