首页 > 编程技术 > android

Android中viewPager+fragment实现滑页效果实例

发布时间:2016-9-20 19:54

在手机app应用中,滑页效果非常常用,本文我们分享在Android应用开发中,用viewPager+fragment如何实现滑动分页效果的实例。

效果图如下,手指在手机向左或者向右滑就可以实现相应的页面切换。


先看activity_main.xml文件,非常简单,主要是三个标题TextView和viewpager

                                    


我们再看看相应的MainActivity需要准备些什么

package com.example.keranbin.view.activity;
import android.app.Activity;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import com.example.keranbin.view.R;
import com.example.keranbin.view.adapter.MFragmentPagerAdapter;
import com.example.keranbin.view.fragment.OneFragment;
import com.example.keranbin.view.fragment.ThreeFragment;
import com.example.keranbin.view.fragment.TwoFragment;
import java.util.ArrayList;
public class MainActivity extends FragmentActivity {
    private ViewPager viewPager;                         //页面viewpager
    private ArrayList fragmentlists;            //fragment集合,
    private MFragmentPagerAdapter mFragmentPagerAdapter;   //viewpager适配器
    private TextView tvOne;
    private TextView tvTwo;
    private TextView tvThree;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();         //初始化页面组件及一些数据
        setListener();      //对页面一些组件设置一些监听事件
    }
    //初始化页面组件及一些数据
    private void initView() {
        viewPager= (ViewPager) this.findViewById(R.id.vp);
        tvOne= (TextView) this.findViewById(R.id.tv_layout_one);
        tvTwo= (TextView) this.findViewById(R.id.tv_layout_two);
        tvThree= (TextView) this.findViewById(R.id.tv_layout_three);
        //初始化one two three的背景
        tvOne.setBackgroundResource(R.drawable.solid_half_elipse_blue_left);
        tvTwo.setBackgroundResource(R.drawable.solid_transparent_rectangle);
        tvThree.setBackgroundResource(R.drawable.solid_half_elipse_transparent_right);
        //往fragment集合里添加fragment
        fragmentlists=new ArrayList();
        OneFragment oneFragment=new OneFragment();
        TwoFragment twoFragment=new TwoFragment();
        ThreeFragment threeFragment=new ThreeFragment();
        fragmentlists.add(oneFragment);
        fragmentlists.add(twoFragment);
        fragmentlists.add(threeFragment);
        
        
        //初始化viewpager适配器
        initViewPagerAdapter();
    }
    //初始化viewpager适配器
    private void initViewPagerAdapter() {
        mFragmentPagerAdapter=new MFragmentPagerAdapter(getSupportFragmentManager(),fragmentlists);
        viewPager.setAdapter(mFragmentPagerAdapter);
        viewPager.setCurrentItem(0);
    }
    //对页面一些组件设置一些监听事件
    private void setListener() {
        //分别对one,two,three三个TextView设置点击监听事件,发生点击事件时改变相应的背景及viewpager的内容
        tvOne.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                tvOne.setBackgroundResource(R.drawable.solid_half_elipse_blue_left);
                tvTwo.setBackgroundResource(R.drawable.solid_transparent_rectangle);
                tvThree.setBackgroundResource(R.drawable.solid_half_elipse_transparent_right);
                viewPager.setCurrentItem(0);
            }
        });
        tvTwo.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                tvOne.setBackgroundResource(R.drawable.solid_half_elipse_transparent_left);
                tvTwo.setBackgroundResource(R.drawable.solid_blue_rectangle);
                tvThree.setBackgroundResource(R.drawable.solid_half_elipse_transparent_right);
                viewPager.setCurrentItem(1);
            }
        });
        tvThree.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                tvOne.setBackgroundResource(R.drawable.solid_half_elipse_transparent_left);
                tvTwo.setBackgroundResource(R.drawable.solid_transparent_rectangle);
                tvThree.setBackgroundResource(R.drawable.solid_half_elipse_blue_right);
                viewPager.setCurrentItem(2);
            }
        });
        //对页面viewpager设置监听事件
        viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            }
            @Override
            public void onPageSelected(int position) {
                //页面滑动时改变"one","two","three"三个TextView的背景颜色
                if(position==0){
                    tvOne.setBackgroundResource(R.drawable.solid_half_elipse_blue_left);
                    tvTwo.setBackgroundResource(R.drawable.solid_transparent_rectangle);
                    tvThree.setBackgroundResource(R.drawable.solid_half_elipse_transparent_right);
                }else if(position==1){
                    tvOne.setBackgroundResource(R.drawable.solid_half_elipse_transparent_left);
                    tvTwo.setBackgroundResource(R.drawable.solid_blue_rectangle);
                    tvThree.setBackgroundResource(R.drawable.solid_half_elipse_transparent_right);
                }else if(position==2){
                    tvOne.setBackgroundResource(R.drawable.solid_half_elipse_transparent_left);
                    tvTwo.setBackgroundResource(R.drawable.solid_transparent_rectangle);
                    tvThree.setBackgroundResource(R.drawable.solid_half_elipse_blue_right);
                }
            }
            @Override
            public void onPageScrollStateChanged(int state) {
            }
        });
    }
}


大体思路是,我们先定义一个页面集合ArrayList,接着建立相应的Fragment装载到集合中,然后定义自己的viewPager适配器,最后调用viewpager的setAdapter即可。

自定义的viewpager适配器代码如下


package com.example.keranbin.view.adapter;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import java.util.ArrayList;
/**
 * Created by keranbin on 2015/10/12.
 */
public class MFragmentPagerAdapter extends FragmentPagerAdapter {
    private ArrayList fragmentlists;
    public MFragmentPagerAdapter(FragmentManager fm,ArrayList fragmentlists) {
        super(fm);
        this.fragmentlists=fragmentlists;
    }
    @Override
    public int getCount() {
        return fragmentlists.size();
    }
    @Override
    public Fragment getItem(int position) {
        return fragmentlists.get(position);
    }
}


在现在的智能手机应用中,推送功能非常重要,在Android应用开发中,Android消息推送不是很简单。下面和大家探讨一种Android消息推送的比较好的解决方案。

1.消息推送基础

消息推送,就是在互联网上通过定期传送用户需要的信息来减少信息过载的一项新技术。推送技术通过自动传送信息给用户,来减少用于网络上搜索的时间。它根据用户的兴趣来搜索、过滤信息,并将其定期推给用户,帮助用户高效率地发掘有价值的信息

当我们开发需要和服务器交互的移动应用时,基本上都需要和服务器进行交互,包括上传数据到服务器,同时从服务器上获取数据。

一般情况下,客户端与服务器之间通讯客户端是主动的,但这就存在一个问题就是一旦服务器数据有更新或者服务器要下发通知给客户端只能等客户端连接的时候才能实现。这种方式使消息失去了实时性。

如何使客户端能够实时的收到服务器的消息和通知,总体来说有两种方式,第一种是客户端使用Pull(拉)的方式,就是隔一段时间就去服务器上获取一下信息,看是否有更新的信息出现。第二种就是 服务器使用Push(推送)的方式,当服务器端有新信息了,则把最新的信息Push到客户端上。这样,客户端就能自动的接收到消息。 

  虽然Pull和Push两种方式都能实现获取服务器端更新信息的功能,但是明显来说Push方式比Pull方式更优越。因为Pull方式更费客户端的网络流量,更主要的是费电量,还需要我们的程序不停地去监测服务端的变化。  

2. 几种常见的解决方案实现原理

  1)轮询(Pull)方式:客户端定时向服务器发送询问消息,一旦服务器有变化则立即同步消息。

  2)SMS(Push)方式:通过拦截SMS消息并且解析消息内容来了解服务器的命令,但这种方式一般用户在经济上很难承受。

  3)持久连接(Push)方式:客户端和服务器之间建立长久连接,这样就可以实现消息的及时行和实时性。

3、消息推送解决方案概述

  A、C2DM云端推送方案

在Android手机平台上,Google提供了C2DM(Cloudto Device Messaging)服务。Android Cloud to Device Messaging (C2DM)是一个用来帮助开发者从服务器向Android应用程序发送数据的服务。该服务提供了一个简单的、轻量级的机制,允许服务器可以通知移动应用程序直接与服务器进行通信,以便于从服务器获取应用程序更新和用户数据。

该方案存在的主要问题是C2DM需要依赖于Google官方提供的C2DM服务器,由于国内的网络环境,这个服务经常不可用。

  B、MQTT协议实现Android推送

  采用MQTT协议实现Android推送功能也是一种解决方案。MQTT是一个轻量级的消息发布/订阅协议,它是实现基于手机客户端的消息推送服务器的理想解决方案。

  wmqtt.jar 是IBM提供的MQTT协议的实现。我们可以从这里(https://github.com/tokudu/AndroidPushNotificationsDemo)下载该项目的实例代码,并且可以找到一个采用PHP书写的服务器端实现(https://github.com/tokudu/PhpMQTTClient)。

  C、RSMB实现推送功能

  Really Small Message Broker (RSMB) ,是一个简单的MQTT代理,同样由IBM提供,其查看地址是:http://www.alphaworks.ibm.com/tech/rsmb。缺省打开1883端口,应用程序当中,它负责接收来自服务器的消息并将其转发给指定的移动设备。SAM是一个针对MQTT写的PHP库。我们可以从这个http://pecl.php.net/package/sam/download/0.2.0地址下载它.

   D、XMPP协议实现Android推送

  Google官方的C2DM服务器底层也是采用XMPP协议进行的封装。XMPP(可扩展通讯和表示协议)是基于可扩展标记语言(XML)的协议,它用于即时消息(IM)以及在线探测。这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息。

  androidpn是一个基于XMPP协议的java开源Android push notification实现。它包含了完整的客户端和服务器端。但也存在一些不足之处:

  1) 比如时间过长时,就再也收不到推送的信息了。

  2)性能上也不够稳定。

3)如果将消息从服务器上推送出去,就不再管理了,不管消息是否成功到达客户端手机上。

如果我们要使用androidpn,则还需要做大量的工作,需要理解XMPP协议、理解Androidpn的实现机制,需要调试内部存在的BUG。

  E、使用第三方平台

  目前国内、国外有一些推送平台可供使用,但是涉及到收费问题、保密问题、服务质量问题、扩展问题等等,又不得不是我们望而却步。

4、消息推送完美方案

      综合以上论述,在建立Android消息推送方面可谓方案多多,但每一款方案都有其优缺点。但无论如何,还是自己搭建一个推送平台是上策。因为你有、他有不如自己有。

         在搭建自有推送平台上建议使用《九日升Android消息推送组件》(http://www.bjjrs.net/product/13629681868537.html)。该组不仅可以拿来即用,并且还可以提供源码以便扩展,实现自己的特殊需求。

       A、推送原理

    九日升Android消息推送组件基于XMPP协议实现Android推送。XMPP(可扩展通讯和表示协议)是基于可扩展标记语言(XML)的协议,它用于即时消息(IM)以及在线探测。这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息。

    九日升Android消息推送组件实现原理见下图:

            a.JPG
                                                  图1-消息推送原理图

    九日升Android消息推送组件由服务器部分和客户端部分组成。每一部分都由XMPP协议组件和外部接口组件构成。XMPP协议组件负责服务器和Android客户端间的连接管理、消息通讯,外部接口组件负责接收应用系统、客户端应用的命令,向应用系统发送接收到的通知消息。

    九日升Android消息组件提供基于Tomcat的服务器应用和Android开发jar包。其中基于Tomcat的服务器应用直接在Tomcat上部署即可,Android开发jar包引入Android项目即可。

   B 集成方式

    1)、服务器部署

    九日升Android消息组件Tomcat的服务器应用直接部署在Tomcat中,端口号任意设定。

    2)、客户端jar包引用

    在Android项目中建立libs目录,然后将提供的Android开发jar包复制到该目录即可。见下图:

b.JPG

                                        

图2-jar包引入图

    3)、Android项目AndroidManifest.xml文件修改

  在该文件中增加以下权限:

     <uses-permission android:name="android.permission.READ_PHONE_STATE" />

     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

     <uses-permission android:name="android.permission.INTERNET" />

     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

     <uses-permission android:name="android.permission.VIBRATE" />

   在该文件中注册服务:

     <service android:enabled="true"

     android:name="com.bjjrs.server.NotificationService"

     android:label="NotificationService">

          <intent-filter>

             <action android:name="com.bjjrs.server.NotificationService" />

         </intent-filter>

    </service>

   至此,九日升Android消息组件集成工作完成。

    C、接口方式

    1)、服务器端接口采用基于http协议的访问方式,采用http协议从服务器中获取各种信息,实现通知消息的推送。

如使用以下方式和参数就可以实现各种用户消息的查询:

  http://localhost:8080/user.do?action=getAllUser&isOnline=&userID=&userType=&deptID=&deptName=&realName=

    使用如下方式就可以实现各种消息的推送:

    http://localhost:8080/notification.do?action=pushNoti&userNames=&title=&content=

    2)、Android客户端接口采用广播机制。

    消息接收:当XMPP协议组件接收到推送消息时,将按照一定格式广播该消息,通知客户端其他应用接收并处理该消息。

    消息发送:客户端应用需要向服务器或者其他客户端发送即时消息时,只需按一定格式广播该消息,XMPP组件就会自动接收该消息并发送到指定的其他客户端。

    D、优势特点

    1)、系统集成简单,无需复杂的设置。

    2)、Android客户端应用和九日升Android消息推送组件完全分离,通过接口相互调用,实现模块应用最优化。

    3)、客户端通讯机制采用广播方式,给客户端应用带来极大的灵活性和可扩展性,可以自由处理接收到的推送消息。

    4)、九日升Android消息推送组件在服务器端具备消息存储、消息重发、消息路由等功能,在客户端部分具备断线重连、、收到确认、阅读确认、消息发送、命令执行等功能,确保消息能够推送到客户端,同时也保证客户端能够收到、阅读消息。

   E、 应用范围

  九日升Android消息推送组件可在以下场景中使用:

    1)、用于消息推送。如:通知下达、应急指挥等。

    2)、用户及时消息交互。如在线聊天、工作情况交互等。

    3)、用于远程控制。如控制远程客户端的状态、数据上报等。


Android消息推送机制


1.推送方式基础知识:

当我们开发需要和服务器交互的应用程序时,基本上都需要获取服务器端的数据,比如《地震应急通》就需要及时获取服务器上最新的地震信息。要获取服务器上不定时更新的信息一般来说有两种方法,第一种是客户端使用Pull(拉)的方式,隔一段时间就去服务器上获取信息,看是否有更新的信息出现。第二种就是服务器使用Push(推送)的方式,当服务器端有新信息了,则把最新的信息Push到客户端上。 

虽然Pull和Push两种方式都能实现获取服务器端更新信息的功能,但是明显来说Push is better than pull。因为Pull方式更费客户端的网络流量,更主要的是费电量。  

在开发Android和iPhone应用程序时,我们往往需要从服务器不定的向手机客户端即时推送各种通知消息,iPhone上已经有了比较简单的和完美的推送通知解决方案,我会在以后详细介绍IPhone中的解决方案,可是Android平台上实现起来却相对比较麻烦,最近利用几天的时间对 Android的推送通知服务进行初步的研究。在Android手机平台上,Google提供了C2DM(Cloudto Device Messaging)服务,起初我就是准备采用这个服务来实现自己手机上的推送功能。  

Android Cloud to Device Messaging (C2DM)是一个用来帮助开发者从服务器向Android应用程序发送数据的服务。该服务提供了一个简单的、轻量级的机制,允许服务器可以通知移动应用程序直接与服务器进行通信,以便于从服务器获取应用程序更新和用户数据。C2DM服务负责处理诸如消息排队等事务并向运行于目标设备上的应用程序分发这些消息。关于C2DM具体使用过程,我会以后的博文中再详细介绍,这里大家先了解下大致方案情况。

C2DM操作过程图:


但是经过一番研究发现,这个服务存在很大的问题:

1)C2DM内置于Android的2.2系统上,无法兼容老的1.6到2.1系统;

2)C2DM需要依赖于Google官方提供的C2DM服务器,由于国内的网络环境,这个服务经常不可用,如果想要很好的使用,我们的App Server必须也在国外,这个恐怕不是每个开发者都能够实现的; 有了上述两个使用上的制约,导致我最终放弃了这个方案,不过我想利用另外一篇文章来详细的介绍C2DM的框架以及客户端和App Server的相应设置方法,可以作为学习与参考之用。即然C2DM无法满足我们的要求,那么我们就需要自己来实现Android手机客户端与App Server之间的通信协议,保证在App Server想向指定的Android设备发送消息时,Android设备能够及时的收到。

2. 几种常见的解决方案

1)轮询(Pull):应用程序应当阶段性的与服务器进行连接并查询是否有新的消息到达,你必须自己实现与服务器之间的通信,例如消息排队等。而且你还要考虑轮询的频率,如果太慢可能导致某些消息的延迟,如果太快,则会大量消耗网络带宽和电池。

2)SMS(Push):在Android平台上,你可以通过拦截SMS消息并且解析消息内容来了解服务器的意图。这是一个不错的想法,我就见过采用这个方案的应用程序。这个方案的好处是,可以实现完全的实时操作。但是问题是这个方案的成本相对比较高,你很难找到免费的短消息发送网关,关于这个方案的实现。

3)持久连接(Push):这个方案可以解决由轮询带来的性能问题,但是还是会消耗手机的电池。Apple的推送服务之所以工作的很好,是因为每一台手机仅仅保持一个与服务器之间的连接,事实上C2DM也是这么工作的。不过这个方案也存在不足,就是我们很难在手机上实现一个可靠的服务。

Android操作系统允许在低内存情况下杀死系统服务,所以你的通知服务很可能被操作系统Kill掉了。前两个方案存在明显的不足,第三个方案也有不足,不过我们可以通过良好的设计来弥补,以便于让该方案可以有效的工作。毕竟,我们要知道GMail,GTalk以及GoogleVoice都可以实现实时更新的。

3. MQTT协议实现Android推送

采用MQTT协议实现Android推送 MQTT是一个轻量级的消息发布/订阅协议,它是实现基于手机客户端的消息推送服务器的理想解决方案。 wmqtt.jar 是IBM提供的MQTT协议的实现。我们可以从这里下载该项目的实例代码,并且可以找到一个采用PHP书写的服务器端实现。

架构如下所示:


wmqtt.jar 是IBM提供的MQTT协议的实现。我们可以从如下站点下载它。你可以将该jar包加入你自己的Android应用程序中。

4.RSMB实现推送:

Really Small Message Broker (RSMB) ,他是一个简单的MQTT代理,同样由IBM提供。缺省打开1883端口,应用程序当中,它负责接收来自服务器的消息并将其转发给指定的移动设备。

SAM是一个针对MQTT写的PHP库。我们可以从这个下载它.

send_mqtt.php是一个通过POST接收消息并且通过SAM将消息发送给RSMB的PHP脚本。

Really Small Message Broker (RSMB) ,他是一个简单的MQTT代理,同样由IBM提供。缺省打开1883端口,应用程序当中,它负责接收来自服务器的消息并将其转发给指定的移动设备。

5. XMPP协议实现Android推送

这是我在项目中采用的方案。事实上Google官方的C2DM服务器底层也是采用XMPP协议进行的封装。 XMPP(可扩展通讯和表示协议)是基于可扩展标记语言(XML)的协议,它用于即时消息(IM)以及在线探测。这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息。

androidpn是一个基于XMPP协议的java开源Android push notification实现,我会在以后的博文中详细介绍androidpn。它包含了完整的客户端和服务器端。经过源代码研究我发现,该服务器端基本是在另外一个开源工程openfire基础上修改实现的,不过比较郁闷的是androidpn的文档是由韩语写的,所以整个研究过程基本都是读源码。

实现意图如下图所示:


androidpn 客户端需要用到一个基于java的开源XMPP协议包asmack,这个包同样也是基于openfire下的另外一个开源项目smack,不过我们不需要自己编译,可以直接把androidpn客户端里面的asmack.jar拿来使用。客户端利用asmack中提供的XMPPConnection类与服务器建立持久连接,并通过该连接进行用户注册和登录认证,同样也是通过这条连接,接收服务器发送的通知。

androidpn服务器端也是java语言实现的,基于openfire开源工程,不过它的Web部分采用的是spring框架,这一点与 openfire是不同的。Androidpn服务器包含两个部分,一个是侦听在5222端口上的XMPP服务,负责与客户端的 XMPPConnection类进行通信,作用是用户注册和身份认证,并发送推送通知消息。另外一部分是Web服务器,采用一个轻量级的HTTP服务器,负责接收用户的Web请求。服务器架构如下:


最上层包含四个组成部分,分别是SessionManager,Auth Manager,PresenceManager以及Notification Manager。SessionManager负责管理客户端与服务器之间的会话,Auth Manager负责客户端用户认证管理,Presence Manager负责管理客户端用户的登录状态,NotificationManager负责实现服务器向客户端推送消息功能。

这个解决方案的最大优势就是简单,我们不需要象C2DM那样依赖操作系统版本,也不会担心某一天Google服务器不可用。利用XMPP协议我们还可以进一步的对协议进行扩展,实现更为完善的功能。采用这个方案,我们目前只能发送文字消息,不过对于推送来说一般足够了,因为我们不能指望通过推送得到所有的数据,一般情况下,利用推送只是告诉手机端服务器发生了某些改变,当客户端收到通知以后,应该主动到服务器获取最新的数据,这样才是推送服务的完整实现。


SwipeRefreshLayout是google自带的Android开发中下拉刷新的控件,本文我们来介绍一下这个SwipeRefreshLayout控件,然后实分享两个实例。

下面App基本都有下拉刷新的功能,以前基本都使用XListView或者自己写一个下拉刷新,近期Google提供了一个官方的下拉刷新控件 SwipeRefreshLayout,我感觉还不错啊,见惯了传统的下拉刷新,这个反而给人耳目一新的感觉(貌似知乎的APP已经使用这种下拉刷新了)。

Google也在官方网站给出了V4的兼容包:



再来看看布局文件里的代码(我这里放的是一个ListView 当然也可以放其他控件 只要你高兴就好)

 <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/main_srl_bloglist"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
        <ListView
            android:id="@+id/main_lv_bolg_list"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:divider="#00000000"
            android:dividerHeight="5dp"></ListView>
    </android.support.v4.widget.SwipeRefreshLayout>


最后来看看Activiy里的代码吧(这里我用的AndroidAnnotations 所以没有写 findViewById 哈哈)

 //设置刷新时动画的颜色,可以设置4个
        mSwipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_light, android.R.color.holo_red_light, android.R.color.holo_orange_light, android.R.color.holo_green_light);
        mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                mIndex=1;
                mBlogList.clear();
                loadBlogData();
            }
        });


在onRefresh 里调用获取数据的方法就好了 数据获取完毕 别忘了 修改状态

mSwipeRefreshLayout.setRefreshing(false);

哈哈 以前要写成吨的代码 用SwipeRefreshLayout 就这几行代码 是不是很方便。



关于Android多线程开发,网上有很多教程,不过本人都觉得那些不够详细系统,本文我们提供了Android开发中多线程详细教程

在Android应用的开发过程中,我们不可避免的要使用多线程,获取服务器数据、下载网络数据、遍历文件目录查找特定文件等等耗时的工作都离不开线程的知识。Android继承了Java的多线程体系,同时又实现了许多更加简易的API来操作线程。通过这些API,我们可以方便快捷的实现线程的创建、线程间的交互。我打算记下最近自己学习Android多线程机制时的学习笔记,一来可以供以后翻阅查看,二来为那些正疑惑与此的朋友提供一条解决问题的途径。

先大致说一下我想写的内容:

一、AsyncTask

二、Thread 与 Handler

三、HandlerThread

全部内容分为3篇Bolg。

下面正式开始第一部分吧!

一、AsyncTask

(0)参考文档:http://developer.android.com/reference/android/os/AsyncTask.html

(1)简单介绍:

  AsyncTask这是一个很便捷的关于线程的API,它将比较耗时(几秒)的工作放在非UI线程中运行,然后将结果的展示放在UI线程中。整个过程不涉及到任何关于Thread、Handler及Message等的处理。

  先来说说AsyncTask的构造吧,它接收3个泛型参数,分别代表了后台工作的传入参数类型、进度的参数类型、结果的参数类型。每一个的具体含义会在接下来的AsyncTask的执行过程中讲到。要想使用AsyncTask就必须拓展为它的子类同时指定3个泛型参数,然后在子类的中重写几个重要的方法。下面就AsyncTask的执行过程来写。

(2)AsyncTask的执行过程:

  AsyncTask中的回调方法有:

onPreExecute(),UI线程中执行,一般用来处理执行异步任务前的准备工作,比如弹出一个进度条,展现一个对话框告知用户正在执行一项任务。

doInBackground(Params...),在非UI线程中执行,这里是异步任务的核心,所有需要耗时的工作全在这里执行,因为不是在UI线程中,因此不会存在阻塞UI线程的危险,不会引发ANR错误。

onProgressUpdate(Progress...),在UI 线程中执行,一般用来更新任务执行的进度,它的调用时在publishProgress(Progress...)调用之后接着被调用的,我们可以在doInBackground(Params...)中多次调用publishProgress(Progress...)来告知用户任务的执行状态。

onPostExecute(Result),在UI线程中执行,任务完成后(doInBackground(Params...)执行结束)且任务没被取消时,会执行该方法,用来处理异步任务得到的结果。

onCancelled(Result),在UI线程中执行,当任务以柔和的方式结束(AsyncTask.cancel(false))这个方法会取代onPostExecute(Result)得以执行(当然这里也要看程序的执行逻辑,有没有在doInBackground(Params...)中检测任务是否被取消)

  关于泛型参数:

  三个泛型参数分别是:Params,Progress,Result。在上面的回调方法接受中我们可以看到它们分别所处那个方法,Params是启动异步任务时传入的参数,比如启动异步任务从网上下载一张图片传入图片的URL地址。Progress是进度参数类型,调用publishProgress(Progress...)时的参数需和Progress类型相容。比如用一个整数去描述进度,Progress及时Integer。Result是结果参数类型,异步任务完成后,如需返回一个结果,那么这个结果的类型就是Result类型。特别地,可以用Void去代替任何一个或多个泛型参数,这样做就以为着不需要参数。

(3)一个简单的例子:

话说百闻不如一见,下面我就用一个极简单的例子说明AsyncTask的使用过程。先说我要干啥,我用一个异步任务来更新一个进度条(简单吧!粗暴吧!颤抖吧,地球人!O(∩_∩)O)

注:示例没什么格式,强迫症患者见谅!!

先给出布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="15dp">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:text="AsyncTask示例"
        android:textSize="30sp"/>
    <ProgressBar
        android:id="@+id/progress"
        style="@android:style/Widget.ProgressBar.Horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"/>
    <Button
        android:id="@+id/cancel"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="cancel"/>
    <Button
        android:id="@+id/start"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@+id/cancel"
        android:text="start"/>
</RelativeLayout>


下面是AsyncTask的逻辑代码:

package comfallblank.github.asynctasktest;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
public class MainActivity extends AppCompatActivity {
    private ProgressBar mProgressBar;
    private Button mStartButton;
    private Button mCancelButton;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mProgressBar = (ProgressBar) findViewById(R.id.progress);
        //设置ProgressBar暂时不可见,一会在异步任务的中显示它。
        mProgressBar.setVisibility(View.GONE);
        final MyTask myTask =  new MyTask();
        mStartButton = (Button) findViewById(R.id.start);
        mStartButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                myTask.execute();
            }
        });
        mCancelButton = (Button) findViewById(R.id.cancel);
        mCancelButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                myTask.cancel(false);
            }
        });
    }
   private class MyTask extends AsyncTask<Void,Integer,String>{
       private static final  String TAG = "MyTask";
       @Override
       protected void onPreExecute() {
           super.onPreExecute();
           //准备工作,显示进度条
           mProgressBar.setVisibility(View.VISIBLE);
           //打印日志,查看执行路径,下同
           Log.d(TAG,"onPreExecute");
       }
       @Override
       protected String doInBackground(Void... voids) {
           Log.d(TAG,"doInBackground");
           //每1s对进度条更新10%
           synchronized (this){
               for (int i = 1;i<=10;i++){
                   try {
                       Thread.sleep(1000*1);
                       publishProgress(i*10);
                       if (isCancelled()){
                           return "Task cancelled";
                       }
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
           }
           return "Task executed";
       }
       @Override
       protected void onProgressUpdate(Integer... values) {
           super.onProgressUpdate(values);
           mProgressBar.setProgress(values[0]);
           Log.d(TAG,"onProgressUpdate");
       }
       @Override
       protected void onPostExecute(String s) {
           super.onPostExecute(s);
           Log.d(TAG, s);
           Log.d(TAG,"onPostExecute");
       }
       @Override
       protected void onCancelled(String s) {
           super.onCancelled(s);
           Log.d(TAG, s);
           Log.d(TAG, "onCancelled");
       }
   }
}


LogCat日志:

执行完毕,没有中途取消任务:

10-06 13:55:05.871 6737-6737/comfallblank.github.asynctasktest D/MyTask: onPreExecute
10-06 13:55:05.891 6737-13040/comfallblank.github.asynctasktest D/MyTask: doInBackground
10-06 13:55:06.922 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:07.923 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:08.914 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:09.925 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:09.975 6737-6753/comfallblank.github.asynctasktest W/art: Suspending all threads took: 18.035ms
10-06 13:55:10.926 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:11.937 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:12.938 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:13.939 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:14.950 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:15.951 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:15.971 6737-6737/comfallblank.github.asynctasktest D/MyTask: Task executed
10-06 13:55:15.971 6737-6737/comfallblank.github.asynctasktest D/MyTask: onPostExecute

中途取消任务:

10-06 13:56:10.644 14129-14129/comfallblank.github.asynctasktest D/MyTask: onPreExecute
10-06 13:56:10.654 14129-14302/comfallblank.github.asynctasktest D/MyTask: doInBackground
10-06 13:56:11.665 14129-14129/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:56:12.666 14129-14129/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:56:13.667 14129-14129/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:56:14.668 14129-14129/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:56:15.689 14129-14129/comfallblank.github.asynctasktest D/MyTask: Task cancelled
10-06 13:56:15.689 14129-14129/comfallblank.github.asynctasktest D/MyTask: onCancelled

关于AsyncTask的一些注意事项:

(1)AsyncTask对象只能使用一次,也就是说一个对象执行完毕或者中途被cancel后,不能再次调用execute().

(2)AsyncTask.execute()只能在主线程中调用。

(3)AsyncTask不能处理并发,多个AsyncTask也是有执行顺序的,且同一个应用的所有AsyncTask都是在同一进程中执行,按照队列排序依次执行。

(4)AsyncTask只能处理那些耗时不是特别长的任务,对于需要长时间执行的任务最好自己实现Thread。

AsyncTask很方便,它屏蔽了所有Thread、Handler、Looper、Message及Messagequene的细节,这或许就是封装的魅力吧!但当你真正明白了Android的线程通讯之后你就会明白那些良好设计带来的美感。


二、Thread 与 Handler

  本节涉及的概念较多,有Thread,Handler,Looper,Message,MessageQuene,对于Looper和MessageQueue只是简单的谈及它们在线程通信中的作用,Thread,Handler及Message我会尽力讲清楚些。

  1.Thread基础:

  1)参考文档:http://developer.android.com/reference/java/lang/Thread.html

  我包括我的好些朋友学东西时都会忽略官方文档的重要性,其中很大的原因是因为英文的缘故吧,但其实看懂文档要求英语能力并不高。看别人写的文章、博客、书籍,听别人讲技术,那始终是经过他人过滤后的知识,而这一切未必是你需要的。前车之鉴固然重要,但并不是每一个老人的话都得听。歪果仁写东西有个特点就是他不仅把原理给你讲清楚了,还爱举列子,该怎么做不建议怎么做都会涉及。说了这么多,就是建议自己去看看Android的开发文档,从Guide到Reference内化出自己的知识体系。

  2)简单介绍:

  看Android的Thread继承树,我们就知道它完全继承了Java的线程体系,在这就不赘言过多的Thread细节了,我就如何开启一个线程讲一讲。开启一个新线程有两种方法,第一种是拓展Thread类,在子类中重写run()方法;第二种是声明Thread对象时传入一个Runnable对象,因为只涉及到一个抽象方法,Runnable对象可以用jdk8的Lambda表达式来代替,这样使得线程的创建变得更加简单。创建好了线程后,如何让线程开始执行呢?调用Thread.run()即可让线程进入待执行队列,当获得CPU时间时它就运行起来了。具体的用法例子会在后面的示例中展现。

  2.Handler基础:

  1)参考文档:http://developer.android.com/reference/android/os/Handler.html

  2)概述:

  之前看过一部美剧《天蝎计划》,在介绍团队负责人时,这样说道”He is our goverment handler“。Android中的Handler就是这样一个概念,它是线程通信的发送者和处理者,线程要进行通信时会让handler发出相应的消息,通过Looper传递,Handler发出的消息会在目的线程中得以执行。再举个栗子吧,这是我在《Android编程权威指南》中看到的,它是这么描述线程通信的:两个线程的通信就好像现实中的两个人通过信件来通信,消息队列(MessageQueue)相对于通信时候的信箱;Message(消息)相当于信件;Looper相当于邮递员,它是MessageQueue的操作者;Handler时线程通信的发出者和处理者;每当Thread想要进行通信,它会让Handler投递一个Message给相应的MessageQueue,Looper会一直循环将MessageQueue里的Message发向它的目的地,到达目的地后Looper通知相应的Handler来处理消息。

  每一个Handler都是和唯一的Looper对象绑定的,也就是说一个Handler既仅可以个一个Looper绑定,但一个Looper可以有好几个Handler与之关联。Looper操作的MessageQueue是Handler取得消息进行处理和发出消息的地方。

  3)使用介绍:

  前面说过每一个Handler都是和特别的Looper绑定好的,同时Handler又是处理消息的地方,所以Handler中既要说明和哪个Looper绑定,又要告知怎么处理消息。所以Handler有4个构造方法,下面我来一一介绍:

    Handler()这是无参数构造方法,它默认和当前线程的Looper绑定,未指定消息的处理方式。
    Handler(Looper looper)它需要一个Looper对象作为参数传入,构成出来的Handler对象将和给定的Looper对象绑定。
    Handler(Handler.Callback callback)它需要一个Handler.Callback对象作为参数传入,构造处理的对象和当前线程的Looper绑定并用传入的Handler.Callback对象来处理消息。
    Handler(Looper looper,(Handler.Callback callback)这是第二种和第三种的结合,构成出一个和指定Looper绑定并用指定Callback的回调方法处理消息的Handler对像
    还有一种使用Handler的方法就是自己拓展Handler类,在子类中实现handlerMessage(Message msg)(这就是接口Callback中的抽象方法),这也是我用的比较多的方式。

  Handler的使用就是发送和处理消息,处理消息是在Callback接口中定义好的,当Looper操作的MessageQueue中有消息时,Looper对通知所有与它绑定的Handler调用handlerMessage(Message msg)去处理消息。那怎么发送消息呢?Hander中有一个方法叫sendMessage(Message msg),当调用这个方法后Handler就往Looper操作的MessageQueue中投递一个Message对象,发送消息就算是完成了。简单吧?so easy!

  关于Handler的其他方法还请查看文档,将主干的部分弄清楚了,指端末节随着使用慢慢就熟络了。一口气吃不成胖子。对了,下面的Message部分还会提及Handler的一些内容。

  3.Message基础:

  1)参考文档:http://developer.android.com/reference/android/os/Message.html

  2)使用介绍:

  线程通信既然叫通信肯定有一个消息的载体,Message就是这个消息的载体,它包含了线程想要通信的全部内容,比如线程运行后得到的结果就和可以包含在Message中。关于Message的构造本来可以按正常的方式构造(就是new一个Message对象,词穷不知道怎么说,就叫它”正常的方式“O(∩_∩)O~),但官方推荐的做法是通过Handler.obtainMessage()获得Message对象,因为这样的Message是从即将被回收的Message中取得的,会避免GC的反复运行和减少运行时的内存消耗。

  Message是消息的携带者,它有许多的携带消息的方法,比如setData(Bundle),Message.obj,Message.arg1,Message.arg2等等,需要特别说明的是Message里有一个公开的整形的全局变量what,即Message.what,它一般用来阐述消息的类型,Handler的handlerMessage(Message msg)通常会先检验Message的what变量,然后在决定如何处理。毕竟一个应用中和主线程通信的不可能只用一个线程,一种消息。

  4.Looper与MessageQueue简单介绍:

  1)参考文档:

  Looper:http://developer.android.com/reference/android/os/Looper.html

  MessageQueue:http://developer.android.com/reference/android/os/MessageQueue.html

  2)简单介绍:

  需要注意的地方就是,一个普通Thread创建时是没有Looper对象和它关联的,我们必须在线程的创建中进行关联,具体做法就是在Thread的创建时调用Looper.prepare()进行绑定,调用Looper.loop()使得与线程绑定的Looper对象开始工作。Looper中有一个巨好用的方法,Looper.getMainLooper(),这个方法直接返回当前应用的UI线程的Looper对象,有了Looper对象就可以往主线程发消息了,一会我在示例中会用到这样方法。

  关于MessageQueue,其实在线程通信中我们并不直接使用它,只需知道我们通过Handler发送出去的消息都是放在它里的就行了,它是一个”第层次“的对象,我们也不能直接往它里添加消息。但对于理解整个线程通信过程还是很重要的。

  5.实践——示例

  说了这么多,是时候用实践检验了!先说说我打算怎么做吧!我打算从UI线程向非UI线程(是不是叫主线程和子线程更好?)发一个消息,然后在非UI线程中处理这个消息(这里我只是打印一下日志),然后从非UI线程向主线程发送一个消息,然后在UI线程中处理这个消息(也是简单的打印一下)。好像有些偷懒,但真正使用也大致是这样的,就是把打印换成具体的任务。好了,开始吧!

  先给出布局吧!

<?xml version="1.0" encoding="utf-8"?>
<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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:text="Thread和Handler"/>
    <Button
        android:id="@+id/send_message"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="发送消息"/>
    <Button
        android:id="@+id/startThread"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@id/send_message"
        android:text="开启子线程"/>
</RelativeLayout>


  布局很简单,就是一个textview和两个button,一个button开启线程,并接受来自子线程的信息,另一个从主线程发出消息给子线程。

  下面给出代码逻辑:

  第一部分是子线程的代码:

package comfallblank.github.threadandhandler;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
/**
 * Created by fallb on 2015/10/7.
 */
public class MyThread extends Thread {
    public static final int MSG_WORKER_THREAD = 100;
    private static final  String TAG = "MyThread";
    private Handler mWorkerHandler;
    private Handler mMainHandler;
    public MyThread(Handler handler) {
        mMainHandler = handler;
        mWorkerHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                if(msg.what == MainActivity.MSG_MAIN){
                    Log.d(TAG,"Message:"+msg.obj);
                }
            }
        };
    }
    @Override
    public void run() {
        Looper.prepare();
        Message msg = mMainHandler.obtainMessage();
        msg.what = MyThread.MSG_WORKER_THREAD;
        msg.obj="子线程发出的消息";
        mMainHandler.sendMessage(msg);
        Looper.loop();
    }
    public Handler getWorkerHandler() {
        return mWorkerHandler;
    }
}

  第二份是主线程的:

package comfallblank.github.threadandhandler;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
    public static final int MSG_MAIN = 100;
    private static final String TAG = "MainActivity";
    private Button mStartThread;
    private Button mSendMessage;
    private Handler mHandler = new MyHandler();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final MyThread thread = new MyThread(mHandler);
        mStartThread = (Button) findViewById(R.id.startThread);
        mStartThread.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.d(TAG, "开启线程");
                thread.start();
            }
        });
        mSendMessage = (Button) findViewById(R.id.send_message);
        mSendMessage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Handler workerHandler = thread.getWorkerHandler();
                Message msg = workerHandler.obtainMessage();
                msg.what = MSG_MAIN;
                msg.obj = "来自主线程的消息";
                workerHandler.sendMessage(msg);
            }
        });
    }
    class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == MyThread.MSG_WORKER_THREAD) {
                Log.d(TAG,"Message:"+msg.obj);
            }
        }
    }
}


  Logcat打印日志如下:

  10-07 19:34:34.424 19671-19671/comfallblank.github.threadandhandler D/MainActivity: 开启线程
  10-07 19:34:34.454 19671-19671/comfallblank.github.threadandhandler D/MainActivity: Message:子线程发出的消息
  10-07 19:34:37.267 19671-19671/comfallblank.github.threadandhandler D/MyThread: Message:来自主线程的消息

  6.关于Handler再说两句:

  Handler对象除了发出消息外,还有一族方法,来发出一个Runnable对象,Handler.post(Runnable runnable)及时其中的一个,它向MessageQueue中添加这个Runnable对象,然后目的线程的MessageQueue会执行该方法。

本文章来为各位介绍一篇关于Android Design Support Library高逼格金钱豹制作教程,希望此教程能够帮助到各位安卓开发新手。

最近项目不是很忙,终于有时间写博客啦,趁这几天用Android的一些新特性重新写了金钱豹的框架,先上看图,和之前的原版相比不知道大家更喜欢哪种。


前几天Devin给我开通了vpn,作为Google脑残粉,晚上默默下载了各种最新的Tools,带着我对Material Design的喜爱,正式开始啃Google官网。吐槽下技术是永远学不完的,实际上两个月前,我已经学了些Material Design,我还没来得及使用,现在那部分内容已经是被淘汰、废弃掉的技术了……

Ok,这次就讲解下NavigationView的使用,这是Android Lolipop中Android Design Support Library带来的Material design Components中推出的,此次的Components中还包含floating labels for editing text, a floating action button, snackbar, tabs。

Google的解释:

The navigation drawer can be an important focal point for identity and navigation within
your app and consistency in the design here can make a considerable difference in how easy your app is to navigate, particularly for first time users.NavigationView makes this easier by providing the framework you need for the navigation drawer as well as the ability to inflate your navigation items through a menu resource.

此前,drawertoolge+actionbar是标配,例如Google自己的GooglePlay,而这次Google的意思是navigationbar更加简易使用,只需要填充menu资源即可实现,基本不需要考虑过多的逻辑了,google已经将Actionbar和废弃了,使用Toolbar来代替,这边顺便说一句经验,也是这几天遇到的一个bug,就是要关注Google废弃的东西,要保持警惕有准备,以前我觉得废弃一个API没什么大不了的,废弃了一样能用,但是在这次最新的更新中,Google废弃了Apache的一个Api,使得我一直使用的异步请求方式不能再使用,那天也算是运气好,正好读Google官方更新内容的时候提到了这个废弃Api和解决方案,否则这个bug根本不可能给解决了,因为这个问题实在太新了,直接搜索的话,基本不可能找到解决方案,只能换一种请求方式了,这也让我更理解为什么大牛们都推荐上Google找答案的原因,这个bug要在百度找到估计还得等个半年一年或者根本找不到。
言归正传,使用navigation需要搭配Drawerlayout一起配合使用


<android.support.v4.widget.DrawerLayout       
 xmlns:android="http://schemas.android.com/apk/res/android"       
 xmlns:app="http://schemas.android.com/apk/res-auto"       
 android:layout_width="match_parent"       
 android:layout_height="match_parent"       
 android:fitsSystemWindows="true">
<!-- your content layout -->
<android.support.design.widget.NavigationView           
 android:layout_width="wrap_content"           
 android:layout_height="match_parent"           
 android:layout_gravity="start"           
 app:headerLayout="@layout/drawer_header"           
 app:menu="@menu/drawer"/>
</android.support.v4.widget.DrawerLayout>

Header的布局和普通xml是一样的,主要就是menu_drawer的编写。
主要是两种方式:1、group内部填充item 2、item可以包含字item

下面是Google官方的示例:

The simplest drawer menus will be a collection of checkable menu items:

<group android:checkableBehavior="single">
    <item
        android:id="@+id/navigation_item_1"
        android:checked="true"
        android:icon="@drawable/ic_android"
        android:title="@string/navigation_item_1"/>
    <item
        android:id="@+id/navigation_item_2"
        android:icon="@drawable/ic_android"
        android:title="@string/navigation_item_2"/>
</group>
The checked item will appear highlighted in the navigation drawer, ensuring the user knows which navigation item is currently selected.

选中的item会自动高亮提示用户。


<item
    android:id="@+id/navigation_subheader"
    android:title="@string/navigation_subheader">
    <menu>
        <item
            android:id="@+id/navigation_sub_item_1"
            android:icon="@drawable/ic_android"
            android:title="@string/navigation_sub_item_1"/>
        <item
            android:id="@+id/navigation_sub_item_2"
            android:icon="@drawable/ic_android"
            android:title="@string/navigation_sub_item_2"/>
    </menu>
</item>

监听:

OnNavigationItemSelectedListener using setNavigationItemSelectedListener(). This provides you with the MenuItem that was clicked, allowing you to handle selection events, changed the checked status, load new content, programmatically close the drawer, or any other actions you may want.
Navigationview的接口是写好的。设置setNavigationItemSelectedListener()的监听,传递参数后,会在OnNavigationItemSelected中将MenuItem传递给用户,在这边实现操作即可。

更多的内容我会在后期继续整理发布,只要时间允许,Google推出新的技术我也会第一时间学习并整理发布

标签:[!--infotagslink--]

您可能感兴趣的文章: