首页 文章 精选 留言 我的

精选列表

搜索[智能解析],共10000篇文章
优秀的个人博客,低调大师

Android Fragment 真正的完全解析(上)

自从Fragment出现,曾经有段时间,感觉大家谈什么都能跟Fragment谈上关系,做什么都要问下Fragment能实现不~~~哈哈,是不是有点过~~~ 本 篇博客力求为大家说明Fragment如何产生,什么是Fragment,Fragment生命周期,如何静态和动态的使用 Fragment,Fragment回退栈,Fragment事务;以及Fragment的一些特殊用途,例如:没有布局的Fragment有何用 处?Fragment如何与Activity交互?Fragment如何创建对话框?Fragment如何与ActionBar集成等等。 1、Fragment的产生与介绍 Android 运行在各种各样的设备中,有小屏幕的手机,超大屏的平板甚至电视。针对屏幕尺寸的差距,很多情况下,都是先针对手机开发一套App,然后拷贝一份,修改布 局以适应平板神马超级大屏的。难道无法做到一个App可以同时适应手机和平板么,当然了,必须有啊。Fragment的出现就是为了解决这样的问题。你可 以把Fragment当成Activity的一个界面的一个组成部分,甚至Activity的界面可以完全有不同的Fragment组成,更帅气的是 Fragment拥有自己的生命周期和接收、处理用户的事件,这样就不必在Activity写一堆控件的事件处理的代码了。更为重要的是,你可以动态的添 加、替换和移除某个Fragment。 2、Fragment的生命周期 Fragment必须是依存与Activity而存在的,因此Activity的生命周期会直接影响到Fragment的生命周期。官网这张图很好的说明了两者生命周期的关系: 可以看到Fragment比Activity多了几个额外的生命周期回调方法:onAttach(Activity)当Fragment与Activity发生关联时调用。onCreateView(LayoutInflater, ViewGroup,Bundle)创建该Fragment的视图onActivityCreated(Bundle)当Activity的onCreate方法返回时调用onDestoryView()与onCreateView想对应,当该Fragment的视图被移除时调用onDetach()与onAttach相对应,当Fragment与Activity关联被取消时调用 注意:除了onCreateView,其他的所有方法如果你重写了,必须调用父类对于该方法的实现, 3、静态的使用Fragment 嘿嘿,终于到使用的时刻了~~ 这是使用Fragment最简单的一种方式,把Fragment当成普通的控件,直接写在Activity的布局文件中。步骤: 1、继承Fragment,重写onCreateView决定Fragemnt的布局 2、在Activity中声明此Fragment,就当和普通的View一样 下面展示一个例子(我使用2个Fragment作为Activity的布局,一个Fragment用于标题布局,一个Fragment用于内容布局): TitleFragment的布局文件: [html] view plain copy <?xmlversion="1.0"encoding="utf-8"?> <RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="45dp" android:background="@drawable/title_bar"> <ImageButton android:id="@+id/id_title_left_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="3dp" android:background="@drawable/showleft_selector"/> <TextView android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" android:text="我不是微信" android:textColor="#fff" android:textSize="20sp" android:textStyle="bold"/> </RelativeLayout> TitleFragment [java] view plain copy packagecom.zhy.zhy_fragments; importandroid.app.Fragment; importandroid.os.Bundle; importandroid.view.LayoutInflater; importandroid.view.View; importandroid.view.View.OnClickListener; importandroid.view.ViewGroup; importandroid.widget.ImageButton; importandroid.widget.Toast; publicclassTitleFragmentextendsFragment { privateImageButtonmLeftMenu; @Override publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer, BundlesavedInstanceState) { Viewview=inflater.inflate(R.layout.fragment_title,container,false); mLeftMenu=(ImageButton)view.findViewById(R.id.id_title_left_btn); mLeftMenu.setOnClickListener(newOnClickListener() { @Override publicvoidonClick(Viewv) { Toast.makeText(getActivity(), "iamanImageButtoninTitleFragment!", Toast.LENGTH_SHORT).show(); } }); returnview; } } 同理还有ContentFragment的其布局文件: [html] view plain copy <?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" android:text="使用Fragment做主面板" android:textSize="20sp" android:textStyle="bold"/> </LinearLayout> [java] view plain copy packagecom.zhy.zhy_fragments; importandroid.app.Fragment; importandroid.os.Bundle; importandroid.view.LayoutInflater; importandroid.view.View; importandroid.view.ViewGroup; publicclassContentFragmentextendsFragment { @Override publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer, BundlesavedInstanceState) { returninflater.inflate(R.layout.fragment_content,container,false); } } MainActivity [java] view plain copy packagecom.zhy.zhy_fragments; importandroid.app.Activity; importandroid.os.Bundle; importandroid.view.Window; publicclassMainActivityextendsActivity { @Override protectedvoidonCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); } } Activity的布局文件: [java] view plain copy <RelativeLayoutxmlns: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"> <fragment android:id="@+id/id_fragment_title" android:name="com.zhy.zhy_fragments.TitleFragment" android:layout_width="fill_parent" android:layout_height="45dp"/> <fragment android:layout_below="@id/id_fragment_title" android:id="@+id/id_fragment_content" android:name="com.zhy.zhy_fragments.ContentFragment" android:layout_width="fill_parent" android:layout_height="fill_parent"/> </RelativeLayout> 是不是把Fragment当成普通的View一样声明在Activity的布局文件中,然后所有控件的事件处理等代码都由各自的Fragment去处理,瞬间觉得Activity好干净有木有~~代码的可读性、复用性以及可维护性是不是瞬间提升了~~~下面看下效果图: 4、动态的使用Fragment 上面已经演示了,最简单的使用Fragment的方式~下面介绍如何动态的添加、更新、以及删除Fragment 为了动态使用Fragment,我们修改一下Actvity的布局文件,中间使用一个FrameLayout,下面添加四个按钮~~~嘿嘿~~不是微信的按钮- -! [html] view plain copy <RelativeLayoutxmlns: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"> <fragment android:id="@+id/id_fragment_title" android:name="com.zhy.zhy_fragments.TitleFragment" android:layout_width="fill_parent" android:layout_height="45dp"/> <include android:id="@+id/id_ly_bottombar" android:layout_width="fill_parent" android:layout_height="55dp" android:layout_alignParentBottom="true" layout="@layout/bottombar"/> <FrameLayout android:id="@+id/id_content" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_above="@id/id_ly_bottombar" android:layout_below="@id/id_fragment_title"/> </RelativeLayout> 底部四个按钮的布局就不贴了,到时看效果图就明白了~~ 下面主Activity [java] view plain copy packagecom.zhy.zhy_fragments; importandroid.app.Activity; importandroid.app.FragmentManager; importandroid.app.FragmentTransaction; importandroid.os.Bundle; importandroid.view.View; importandroid.view.View.OnClickListener; importandroid.view.Window; importandroid.widget.LinearLayout; publicclassMainActivityextendsActivityimplementsOnClickListener { privateLinearLayoutmTabWeixin; privateLinearLayoutmTabFriend; privateContentFragmentmWeixin; privateFriendFragmentmFriend; @Override protectedvoidonCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); //初始化控件和声明事件 mTabWeixin=(LinearLayout)findViewById(R.id.tab_bottom_weixin); mTabFriend=(LinearLayout)findViewById(R.id.tab_bottom_friend); mTabWeixin.setOnClickListener(this); mTabFriend.setOnClickListener(this); //设置默认的Fragment setDefaultFragment(); } privatevoidsetDefaultFragment() { FragmentManagerfm=getFragmentManager(); FragmentTransactiontransaction=fm.beginTransaction(); mWeixin=newContentFragment(); transaction.replace(R.id.id_content,mWeixin); transaction.commit(); } @Override publicvoidonClick(Viewv) { FragmentManagerfm=getFragmentManager(); //开启Fragment事务 FragmentTransactiontransaction=fm.beginTransaction(); switch(v.getId()) { caseR.id.tab_bottom_weixin: if(mWeixin==null) { mWeixin=newContentFragment(); } //使用当前Fragment的布局替代id_content的控件 transaction.replace(R.id.id_content,mWeixin); break; caseR.id.tab_bottom_friend: if(mFriend==null) { mFriend=newFriendFragment(); } transaction.replace(R.id.id_content,mFriend); break; } //transaction.addToBackStack(); //事务提交 transaction.commit(); } } 可以看到我们使用FragmentManager对Fragment进行了动态的加载,这里使用的是replace方法~~下一节我会详细介绍FragmentManager的常用API。 注: 如果使用Android3.0以下的版本,需要引入v4的包,然后Activity继承FragmentActivity,然后通过 getSupportFragmentManager获得FragmentManager。不过还是建议版Menifest文件的uses-sdk的 minSdkVersion和targetSdkVersion都改为11以上,这样就不必引入v4包了。 代码中间还有两个Fragment的子类,ContentFragment上面已经见过,FriendFragment其实类似: [java] view plain copy packagecom.zhy.zhy_fragments; importandroid.app.Fragment; importandroid.os.Bundle; importandroid.view.LayoutInflater; importandroid.view.View; importandroid.view.ViewGroup; publicclassFriendFragmentextendsFragment { @Override publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer, BundlesavedInstanceState) { returninflater.inflate(R.layout.fragment_friend,container,false); } } 效果图: 可以看到很好的实现了效果,其实这个效果以前的博客中也出现过,在博客:Android项目Tab类型主界面大总结 Fragment+TabPageIndicator+ViewPager,有兴趣可以看看。ps:为了代码的简洁,就不添加按钮的点击变化什么的了,主要讲解功能了~~~ 5、Fragment家族常用的API Fragment常用的三个类: android.app.Fragment 主要用于定义Fragment android.app.FragmentManager 主要用于在Activity中操作Fragment android.app.FragmentTransaction 保证一些列Fragment操作的原子性,熟悉事务这个词,一定能明白~ a、获取FragmentManage的方式: getFragmentManager() // v4中,getSupportFragmentManager b、主要的操作都是FragmentTransaction的方法 FragmentTransaction transaction = fm.benginTransatcion();//开启一个事务 transaction.add() 往Activity中添加一个Fragment transaction.remove() 从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈(回退栈后面会详细说),这个Fragment实例将会被销毁。 transaction.replace() 使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体~ replace()参数中的布局ID必须是viewgroup子类的ID transaction.hide() 隐藏当前的Fragment,仅仅是设为不可见,并不会销毁 transaction.show() 显示之前隐藏的Fragment detach() 会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。 attach() 重建view视图,附加到UI上并显示。 transatcion.commit()//提交一个事务 注意:常用Fragment的哥们,可能会经常遇到这样Activity状态不一致:State loss这样的错误。主要是因为:commit方法一定要在Activity.onSaveInstance()之前调用。 上述,基本是操作Fragment的所有的方式了,在一个事务开启到提交可以进行多个的添加、移除、替换等操作。 值得注意的是:如果你喜欢使用Fragment,一定要清楚这些方法,哪个会销毁视图,哪个会销毁实例,哪个仅仅只是隐藏,这样才能更好的使用它们。 a、 比如:我在FragmentA中的EditText填了一些数据,当切换到FragmentB时,如果希望会到A还能看到数据,则适合你的就是hide和 show;也就是说,希望保留用户操作的面板,你可以使用hide和show,当然了不要使劲在那new实例,进行下非null判断。 b、再比如:我不希望保留用户操作,你可以使用remove(),然后add();或者使用replace()这个和remove,add是相同的效果。 c、 remove和detach有一点细微的区别,在不考虑回退栈的情况下,remove会销毁整个Fragment实例,而detach则只是销毁其视图结 构,实例并不会被销毁。那么二者怎么取舍使用呢?如果你的当前Activity一直存在,那么在不希望保留用户操作的时候,你可以优先使用detach。 上述已经介绍完成了Fragment常用的一些方法,相信看完,大家一定清楚了Fragment的产生理由,以及如何使用Fragment,再根据API的讲解,也能明白,曾经为何觉得Fragment会出现一些列乱七八槽的问题,终究是因为没有弄清楚其生命周期。 由于篇幅原因,剩下的内容留到下一篇了。在下一篇,会介绍: 1、如何管理Fragment回退栈 2、Fragment如何与Activity交互 3、Fragment与Activity交互的最佳实践 4、没有视图的Fragment的用处 5、使用Fragment创建对话框 6、如何与ActionBar,MenuItem集成等~~ 借鉴:http://blog.csdn.net/lmj623565791/article/details/37970961 本文转自 一点点征服 博客园博客,原文链接http://www.cnblogs.com/ldq2016/p/5318747.html:,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android Fragment 真正的完全解析(下)

1、管理Fragment回退栈 类似与Android系统为Activity维护一个任务栈,我们也可以通过Activity维护一个回退栈来保存每次Fragment事务发生的变化。如果你将Fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次的保存的Fragment。一旦Fragment完全从后退栈中弹出,用户再次点击后退键,则退出当前Activity。 看这样一个效果图: 点击第一个按钮,切换到第二个界面,点击第二个按钮,切换到第三个界面,然后点击Back键依次回退。这像不像初学Android时的Activity跳转,当然了,这里肯定不是,不然我就跪了。这里是Fragment实现的,用户点击Back,实际是Fragment回退栈不断的弹栈。 如何添加一个Fragment事务到回退栈: FragmentTransaction.addToBackStack(String) 下面讲解代码:很明显一共是3个Fragment和一个Activity. 先看Activity的布局文件: [html] view plain copy <RelativeLayoutxmlns: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"> <FrameLayout android:id="@+id/id_content" android:layout_width="fill_parent" android:layout_height="fill_parent"> </FrameLayout> </RelativeLayout> 不同的Fragment就在这个FrameLayout中显示。 MainActivity.java [java] view plain copy packagecom.zhy.zhy_fragments; importandroid.app.Activity; importandroid.app.FragmentManager; importandroid.app.FragmentTransaction; importandroid.os.Bundle; importandroid.view.Window; publicclassMainActivityextendsActivity { @Override protectedvoidonCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); FragmentManagerfm=getFragmentManager(); FragmentTransactiontx=fm.beginTransaction(); tx.add(R.id.id_content,newFragmentOne(),"ONE"); tx.commit(); } } 很简单,直接将FragmentOne添加到布局文件中的FrameLayout中,注意这里并没有调用FragmentTransaction.addToBackStack(String),因为我不喜欢在当前显示时,点击Back键出现白板。而是正确的相应Back键,即退出我们的Activity. 下面是FragmentOne [java] view plain copy packagecom.zhy.zhy_fragments; importandroid.app.Fragment; importandroid.app.FragmentManager; importandroid.app.FragmentTransaction; importandroid.os.Bundle; importandroid.view.LayoutInflater; importandroid.view.View; importandroid.view.View.OnClickListener; importandroid.view.ViewGroup; importandroid.widget.Button; publicclassFragmentOneextendsFragmentimplementsOnClickListener { privateButtonmBtn; @Override publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer, BundlesavedInstanceState) { Viewview=inflater.inflate(R.layout.fragment_one,container,false); mBtn=(Button)view.findViewById(R.id.id_fragment_one_btn); mBtn.setOnClickListener(this); returnview; } @Override publicvoidonClick(Viewv) { FragmentTwofTwo=newFragmentTwo(); FragmentManagerfm=getFragmentManager(); FragmentTransactiontx=fm.beginTransaction(); tx.replace(R.id.id_content,fTwo,"TWO"); tx.addToBackStack(null); tx.commit(); } } 我们在点击FragmentOne中的按钮时,使用了replace方法,如果你看了前一篇博客,一定记得replace是remove和add的合体,并且如果不添加事务到回退栈,前一个Fragment实例会被销毁。这里很明显,我们调用tx.addToBackStack(null);将当前的事务添加到了回退栈,所以FragmentOne实例不会被销毁,但是视图层次依然会被销毁,即会调用onDestoryView和onCreateView,证据就是:仔细看上面的效果图,我们在跳转前在文本框输入的内容,在用户Back得到第一个界面的时候不见了。 接下来FragmentTwo [java] view plain copy packagecom.zhy.zhy_fragments; importandroid.app.Fragment; importandroid.app.FragmentManager; importandroid.app.FragmentTransaction; importandroid.os.Bundle; importandroid.view.LayoutInflater; importandroid.view.View; importandroid.view.View.OnClickListener; importandroid.view.ViewGroup; importandroid.widget.Button; publicclassFragmentTwoextendsFragmentimplementsOnClickListener { privateButtonmBtn; @Override publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer, BundlesavedInstanceState) { Viewview=inflater.inflate(R.layout.fragment_two,container,false); mBtn=(Button)view.findViewById(R.id.id_fragment_two_btn); mBtn.setOnClickListener(this); returnview; } @Override publicvoidonClick(Viewv) { FragmentThreefThree=newFragmentThree(); FragmentManagerfm=getFragmentManager(); FragmentTransactiontx=fm.beginTransaction(); tx.hide(this); tx.add(R.id.id_content,fThree,"THREE"); //tx.replace(R.id.id_content,fThree,"THREE"); tx.addToBackStack(null); tx.commit(); } } 这里点击时,我们没有使用replace,而是先隐藏了当前的Fragment,然后添加了FragmentThree的实例,最后将事务添加到回退栈。这样做的目的是为了给大家提供一种方案:如果不希望视图重绘该怎么做,请再次仔细看效果图,我们在FragmentTwo的EditText填写的内容,用户Back回来时,数据还在~~~ 最后FragmentThree就是简单的Toast了: [java] view plain copy packagecom.zhy.zhy_fragments; importandroid.app.Fragment; importandroid.os.Bundle; importandroid.view.LayoutInflater; importandroid.view.View; importandroid.view.View.OnClickListener; importandroid.view.ViewGroup; importandroid.widget.Button; importandroid.widget.Toast; publicclassFragmentThreeextendsFragmentimplementsOnClickListener { privateButtonmBtn; @Override publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer, BundlesavedInstanceState) { Viewview=inflater.inflate(R.layout.fragment_three,container,false); mBtn=(Button)view.findViewById(R.id.id_fragment_three_btn); mBtn.setOnClickListener(this); returnview; } @Override publicvoidonClick(Viewv) { Toast.makeText(getActivity(),"iamabtninFragmentthree", Toast.LENGTH_SHORT).show(); } } 好了,经过上面的介绍,应该已经知道Fragment回退栈是怎么一回事了,以及hide,replace等各自的应用的场景。 这里极其注意一点:上面的整体代码不具有任何参考价值,纯粹为了显示回退栈,在后面讲解了Fragment与Activity通信以后,会重构上面的代码! 2、Fragment与Activity通信 因为所有的Fragment都是依附于Activity的,所以通信起来并不复杂,大概归纳为: a、如果你Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法 b、如果Activity中未保存任何Fragment的引用,那么没关系,每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentByTag()或者findFragmentById()获得任何Fragment实例,然后进行操作。 c、在Fragment中可以通过getActivity得到当前绑定的Activity的实例,然后进行操作。 注:如果在Fragment中需要Context,可以通过调用getActivity(),如果该Context需要在Activity被销毁后还存在,则使用getActivity().getApplicationContext()。 3、Fragment与Activity通信的最佳实践 因为要考虑Fragment的重复使用,所以必须降低Fragment与Activity的耦合,而且Fragment更不应该直接操作别的Fragment,毕竟Fragment操作应该由它的管理者Activity来决定。 下面我通过两种方式的代码,分别重构,FragmentOne和FragmentTwo的点击事件,以及Activity对点击事件的响应: 首先看FragmentOne [java] view plain copy packagecom.zhy.zhy_fragments; importandroid.app.Fragment; importandroid.os.Bundle; importandroid.view.LayoutInflater; importandroid.view.View; importandroid.view.View.OnClickListener; importandroid.view.ViewGroup; importandroid.widget.Button; publicclassFragmentOneextendsFragmentimplementsOnClickListener { privateButtonmBtn; /** *设置按钮点击的回调 *@authorzhy * */ publicinterfaceFOneBtnClickListener { voidonFOneBtnClick(); } @Override publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer, BundlesavedInstanceState) { Viewview=inflater.inflate(R.layout.fragment_one,container,false); mBtn=(Button)view.findViewById(R.id.id_fragment_one_btn); mBtn.setOnClickListener(this); returnview; } /** *交给宿主Activity处理,如果它希望处理 */ @Override publicvoidonClick(Viewv) { if(getActivity()instanceofFOneBtnClickListener) { ((FOneBtnClickListener)getActivity()).onFOneBtnClick(); } } } 可以看到现在的FragmentOne不和任何Activity耦合,任何Activity都可以使用;并且我们声明了一个接口,来回调其点击事件,想要管理其点击事件的Activity实现此接口就即可。可以看到我们在onClick中首先判断了当前绑定的Activity是否实现了该接口,如果实现了则调用。 再看FragmentTwo [java] view plain copy packagecom.zhy.zhy_fragments; importandroid.app.Fragment; importandroid.os.Bundle; importandroid.view.LayoutInflater; importandroid.view.View; importandroid.view.View.OnClickListener; importandroid.view.ViewGroup; importandroid.widget.Button; publicclassFragmentTwoextendsFragmentimplementsOnClickListener { privateButtonmBtn; privateFTwoBtnClickListenerfTwoBtnClickListener; publicinterfaceFTwoBtnClickListener { voidonFTwoBtnClick(); } //设置回调接口 publicvoidsetfTwoBtnClickListener(FTwoBtnClickListenerfTwoBtnClickListener) { this.fTwoBtnClickListener=fTwoBtnClickListener; } @Override publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer, BundlesavedInstanceState) { Viewview=inflater.inflate(R.layout.fragment_two,container,false); mBtn=(Button)view.findViewById(R.id.id_fragment_two_btn); mBtn.setOnClickListener(this); returnview; } @Override publicvoidonClick(Viewv) { if(fTwoBtnClickListener!=null) { fTwoBtnClickListener.onFTwoBtnClick(); } } } 与FragmentOne极其类似,但是我们提供了setListener这样的方法,意味着Activity不仅需要实现该接口,还必须显示调用mFTwo.setfTwoBtnClickListener(this)。 最后看Activity : [java] view plain copy packagecom.zhy.zhy_fragments; importandroid.app.Activity; importandroid.app.FragmentManager; importandroid.app.FragmentTransaction; importandroid.os.Bundle; importandroid.view.Window; importcom.zhy.zhy_fragments.FragmentOne.FOneBtnClickListener; importcom.zhy.zhy_fragments.FragmentTwo.FTwoBtnClickListener; publicclassMainActivityextendsActivityimplementsFOneBtnClickListener, FTwoBtnClickListener { privateFragmentOnemFOne; privateFragmentTwomFTwo; privateFragmentThreemFThree; @Override protectedvoidonCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); mFOne=newFragmentOne(); FragmentManagerfm=getFragmentManager(); FragmentTransactiontx=fm.beginTransaction(); tx.add(R.id.id_content,mFOne,"ONE"); tx.commit(); } /** *FragmentOne按钮点击时的回调 */ @Override publicvoidonFOneBtnClick() { if(mFTwo==null) { mFTwo=newFragmentTwo(); mFTwo.setfTwoBtnClickListener(this); } FragmentManagerfm=getFragmentManager(); FragmentTransactiontx=fm.beginTransaction(); tx.replace(R.id.id_content,mFTwo,"TWO"); tx.addToBackStack(null); tx.commit(); } /** *FragmentTwo按钮点击时的回调 */ @Override publicvoidonFTwoBtnClick() { if(mFThree==null) { mFThree=newFragmentThree(); } FragmentManagerfm=getFragmentManager(); FragmentTransactiontx=fm.beginTransaction(); tx.hide(mFTwo); tx.add(R.id.id_content,mFThree,"THREE"); //tx.replace(R.id.id_content,fThree,"THREE"); tx.addToBackStack(null); tx.commit(); } } 代码重构结束,与开始的效果一模一样。上面两种通信方式都是值得推荐的,随便选择一种自己喜欢的。这里再提一下:虽然Fragment和Activity可以通过getActivity与findFragmentByTag或者findFragmentById,进行任何操作,甚至在Fragment里面操作另外的Fragment,但是没有特殊理由是绝对不提倡的。Activity担任的是Fragment间类似总线一样的角色,应当由它决定Fragment如何操作。另外虽然Fragment不能响应Intent打开,但是Activity可以,Activity可以接收Intent,然后根据参数判断显示哪个Fragment。 4、如何处理运行时配置发生变化 运行时配置发生变化,最常见的就是屏幕发生旋转,如果你不知道如何处理屏幕变化可以参考:Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案 这里提一下:很多人觉得强制设置屏幕的方向就可以了,但是有一点,当你的应用被至于后台(例如用户点击了home),长时间没有返回的时候,你的应用也会被重新启动。比如上例:如果你把上面的例子你至于FragmentThree界面,然后处于后台状态,长时间后你会发现当你再次通过home打开时,上面FragmentThree与FragmentOne叠加在一起,这就是因为你的Activity重新启动,在原来的FragmentThree上又绘制了一个FragmentOne。 好了,下面看一段代码: Activity: [java] view plain copy packagecom.zhy.zhy_fragments; importandroid.app.Activity; importandroid.app.FragmentManager; importandroid.app.FragmentTransaction; importandroid.os.Bundle; importandroid.view.Window; publicclassMainActivityextendsActivity { privateFragmentOnemFOne; @Override protectedvoidonCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); mFOne=newFragmentOne(); FragmentManagerfm=getFragmentManager(); FragmentTransactiontx=fm.beginTransaction(); tx.add(R.id.id_content,mFOne,"ONE"); tx.commit(); } } Fragment [java] view plain copy packagecom.zhy.zhy_fragments; importandroid.app.Fragment; importandroid.os.Bundle; importandroid.util.Log; importandroid.view.LayoutInflater; importandroid.view.View; importandroid.view.ViewGroup; publicclassFragmentOneextendsFragment { privatestaticfinalStringTAG="FragmentOne"; @Override publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer, BundlesavedInstanceState) { Log.e(TAG,"onCreateView"); Viewview=inflater.inflate(R.layout.fragment_one,container,false); returnview; } @Override publicvoidonCreate(BundlesavedInstanceState) { //TODOAuto-generatedmethodstub super.onCreate(savedInstanceState); Log.e(TAG,"onCreate"); } @Override publicvoidonDestroyView() { //TODOAuto-generatedmethodstub super.onDestroyView(); Log.e(TAG,"onDestroyView"); } @Override publicvoidonDestroy() { //TODOAuto-generatedmethodstub super.onDestroy(); Log.e(TAG,"onDestroy"); } } 很简单的代码,当你运行之后,不断的旋转屏幕,你会发现每旋转一次屏幕,屏幕上就多了一个FragmentOne的实例,并且后台log会打印出许多套生命周期的回调。 类似: [html] view plain copy 07-2008:18:46.651:E/FragmentOne(1633):onCreate 07-2008:18:46.651:E/FragmentOne(1633):onCreate 07-2008:18:46.651:E/FragmentOne(1633):onCreate 07-2008:18:46.681:E/FragmentOne(1633):onCreateView 07-2008:18:46.831:E/FragmentOne(1633):onCreateView 07-2008:18:46.891:E/FragmentOne(1633):onCreateView 这是为什么呢,因为当屏幕发生旋转,Activity发生重新启动,默认的Activity中的Fragment也会跟着Activity重新创建;这样造成当旋转的时候,本身存在的Fragment会重新启动,然后当执行Activity的onCreate时,又会再次实例化一个新的Fragment,这就是出现的原因。 那么如何解决呢: 其实通过检查onCreate的参数Bundle savedInstanceState就可以判断,当前是否发生Activity的重新创建: 默认的savedInstanceState会存储一些数据,包括Fragment的实例:通过打印可以看出: [java] view plain copy 07-2008:23:12.952:E/FragmentOne(1782):Bundle[{android:fragments=android.app.FragmentManagerState@40d0b7b8,android:viewHierarchyState=Bundle[{android:focusedViewId=2131230721,android:views=android.util.SparseArray@40d0af68}]}] 所以,我们简单改一下代码,只有在savedInstanceState==null时,才进行创建Fragment实例: [java] view plain copy packagecom.zhy.zhy_fragments; importandroid.app.Activity; importandroid.app.FragmentManager; importandroid.app.FragmentTransaction; importandroid.os.Bundle; importandroid.util.Log; importandroid.view.Window; publicclassMainActivityextendsActivity { privatestaticfinalStringTAG="FragmentOne"; privateFragmentOnemFOne; @Override protectedvoidonCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); Log.e(TAG,savedInstanceState+""); if(savedInstanceState==null) { mFOne=newFragmentOne(); FragmentManagerfm=getFragmentManager(); FragmentTransactiontx=fm.beginTransaction(); tx.add(R.id.id_content,mFOne,"ONE"); tx.commit(); } } } 现在无论进行多次旋转都只会有一个Fragment实例在Activity中。 现在还存在一个问题,就是重新绘制时,Fragment发生重建,原本的数据如何保持? 其实和Activity类似,Fragment也有onSaveInstanceState的方法,在此方法中进行保存数据,然后在onCreate或者onCreateView或者onActivityCreated进行恢复都可以。 由于篇幅原因,就不贴测试代码了。 5、Fragmeny与ActionBar和MenuItem集成 Fragment可以添加自己的MenuItem到Activity的ActionBar或者可选菜单中。 a、在Fragment的onCreate中调用 setHasOptionsMenu(true); b、然后在Fragment子类中实现onCreateOptionsMenu c、如果希望在Fragment中处理MenuItem的点击,也可以实现onOptionsItemSelected;当然了Activity也可以直接处理该MenuItem的点击事件。 代码: Fragment [java] view plain copy packagecom.zhy.zhy_fragments; importandroid.app.Fragment; importandroid.os.Bundle; importandroid.view.LayoutInflater; importandroid.view.Menu; importandroid.view.MenuInflater; importandroid.view.MenuItem; importandroid.view.View; importandroid.view.ViewGroup; importandroid.widget.Toast; publicclassFragmentOneextendsFragment { @Override publicvoidonCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); } @Override publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer, BundlesavedInstanceState) { Viewview=inflater.inflate(R.layout.fragment_one,container,false); returnview; } @Override publicvoidonCreateOptionsMenu(Menumenu,MenuInflaterinflater) { inflater.inflate(R.menu.fragment_menu,menu); } @Override publicbooleanonOptionsItemSelected(MenuItemitem) { switch(item.getItemId()) { caseR.id.id_menu_fra_test: Toast.makeText(getActivity(),"FragmentMenuItem1",Toast.LENGTH_SHORT).show(); break; } returntrue; } } Activity [java] view plain copy packagecom.zhy.zhy_fragments; importandroid.app.Activity; importandroid.app.FragmentManager; importandroid.app.FragmentTransaction; importandroid.os.Bundle; importandroid.util.Log; importandroid.view.Menu; importandroid.view.MenuItem; importandroid.view.Window; importandroid.widget.Toast; publicclassMainActivityextendsActivity { privatestaticfinalStringTAG="FragmentOne"; privateFragmentOnemFOne; @Override protectedvoidonCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); Log.e(TAG,savedInstanceState+""); if(savedInstanceState==null) { mFOne=newFragmentOne(); FragmentManagerfm=getFragmentManager(); FragmentTransactiontx=fm.beginTransaction(); tx.add(R.id.id_content,mFOne,"ONE"); tx.commit(); } } @Override publicbooleanonCreateOptionsMenu(Menumenu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.main,menu); returntrue; } @Override publicbooleanonOptionsItemSelected(MenuItemitem) { switch(item.getItemId()) { caseR.id.action_settings: Toast.makeText(this,"setting",Toast.LENGTH_SHORT).show(); returntrue; default: //如果希望Fragment自己处理MenuItem点击事件,一定不要忘了调用super.xxx returnsuper.onOptionsItemSelected(item); } } } 效果图: 好了,可以很好的看到,Fragment可以添加MenuItem,也可以自己处理点击~~~ 6、没有布局的Fragment的作用 没有布局文件Fragment实际上是为了保存,当Activity重启时,保存大量数据准备的 请参考博客:Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案 7、使用Fragment创建对话框 这是Google推荐的方式,我也单独写过博客介绍,请参考:Android 官方推荐 : DialogFragment 创建对话框 好了,终于把Fragment相关的联系到一起了,上述基本包含了Fragment所有的用法~~~相信大家如果能够看完,一定有不少的收获~~~ 本文转自农夫山泉别墅博客园博客,原文链接:http://www.cnblogs.com/yaowen/p/4936646.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

VMware vSphere 5.1 群集深入解析(一)

版权 Copyright?2012byDuncanEppingandFrankDenneman. 关于作者 DuncanEpping是VMware技术市场部首席架构师,Dun是vSphereHA和VMwarevCloud云套件的架构设计师,是VMware公司首批认证的设计专家(VCDX007)……(这里就不多译) Tim2009认为这是VMware虚拟化方面的一本专业好书,故使用google、有道、大学英语结合虚拟化项目实践翻译成中文,希望有用。 知识点 这本书是关于VMware产品的,仅代表个人观点,文章中的内容没有经过VMware公司的核准,也没有必要体现VMware的观点,这是作者的书,不是VMware的书 首先我们非常感谢VMware管理团队(KaushikBanerjee,JimSenicka,BogomilBalkanskyandRaghuRaghuram)对此书及相关项目的支持。 前言 数据中心通常都需要经过仔细的配置,设计,监控和其它操作任务,从而实现高效的运行应用程序。但是,这些活动的应用根本上是保证灯常亮,没有大量附加价值,未使IT服务成业务的核心目标,很多任务手工完成设置。假如应用程序的拥有者定义了高水准的商业需求,IT部门会转换为系统的层面,用底层的基础设施解释这些策略需求,并自动配置,管理和操作底层的系统去匹配策略目标,这就是VMware的目标,新技术的解决方案建立新一代数据中心。 数据中心被独立分开成二层-我们可以考虑的是做什么和怎么做,上层的区域运行他们的工作同时匹配策略目标-这就解释了做什么,VMware的目标是让事情尽可能的自动化,应用的拥有者不用精确的了解到虚拟机在哪台物理主机上运行,或者虚拟机应用了哪些有效的规则,应用的拥有者希望适当的设计和配置替代指定需求和期望,那样就可以提供服务来满足这些需求 下层的关于“怎么做”,这些核心的策略如群集高可用性,动态资源分配定义到这层,并被程序控制管理。有了群集我们不再需要单台服务器为工作单位,这就是这本书的主题和为什么这对vSphere部署成功如此重要,VMwarevSphere需要配置合适的群集级别去管理资源,也是为了达到更高服务水平所必须的,群集控制大量的服务器形成池从而协调资源分配,这些工作需求可能包括可用性,存储I/O需求和其它与应用拥有者相关的规格 因此,新时代的数据中心不仅仅是因为虚拟化,它整合了许多传统基础因素如计算,存储,网络等组件,让它们得以系统的管理,这个意思是说以前需要手工的配置,更改,发布,现在都被系统的管理起来,某些项目和虚拟化软件架构一样被具体化,现代化的软件为这些提供了灵活性和高效性,当工作需求改变,底层架构也随之能够适应和扩展来满足需求。 在很长一段时间里,我们期望越来越多的传统基础架构能够得到系统的管理,高水平的架构就像一块补丁,以应用程序为中心已经成为标准服务的一部分,扩展最好是在系统的层面。 网络也变得越来越有代表性,打个比方,安全规则将被分配给应用,而形成对比的是特有的设备被看成网络端口组,类似的创新也发生在存储领域,我们考虑虚拟机作为数据存储的单元,我们计划从物理存储器上迁移相关联的数据,如LUNs或者文件服务器上。 云计算的目标是所有类似的基础设备继续被动态的方式管理,细致的底层设计需要专业的知识,提供这样一本值得读的书,可以帮助IT管理员更集中精力去配置应用来满足商业需求,而不仅仅是维护IT基础设施保证机器灯亮。 我们希望你能在这本书中发现有作用和有意义的地方 RaghuRaghuram ExecutiveVicePresident,CloudInfrastructureandManagement VMware 第一部分vSphere高可用性 第一章介绍vSphere高可用性 当提供服务时,可用性是传统数据中心需要考虑的重要的方面之一,当在VMwarevSphere共享平台上提供服务时,为了面对停机时间的成倍增长,VMware设计了一个VMwarevSphereHA(高可用性)的功能,VMwarevSphereHA(高可用性)以下简称HA,它提供了一个简单且性价比高的解决方案,从而提高虚拟机上应用的高可用性且无关虚拟机的操作系统。vCenterServer的简单几步即可完成HA配置,同时提供统一的和简便的接口,在ESXi或者ESX服务器建立群集时,你可以开启HA功能,我们将在这本书中用ESXi服务器作为参照,也就是说以ESXi服务器为标准.功能的启用可以保护你们的虚拟机,当群集中一台主机出现故障,受影响的虚拟机会自动在同一个群集中其它的ESXi主机上重新启动。 图1:HA的活动流程 另外,当虚拟机的操作系统发生故障时,HA能够重新启动虚拟机上的操作系统,这个功能被称之为虚拟机监测,但有时候也称之为VM的HA,这听起来相当的复杂但也可以通过鼠标点击实现。 图2:点击HA的监测级别 @莫斯和本人分别做了一个实验,首先开启虚拟机监控为<虚拟机和应用程序监控>,然后在虚拟机选项中选择相应的虚拟机匹配不同的<主机隔离响应>,测试是否生效。 实验结果如下表 可以看出,虚拟机的原始状态跟主机隔离响应的策略无关,我俩还是蛮有疑惑的。 不像其它的群集解决方案,HA是一个相当简单的解决方案,只需要依次点击5次鼠标,另外,HA被广泛应用在所有的环境中,但是,HA和微软的MSCS(群集服务)不一样,它不是一个1:1的备份解决方案,MSCS和HA的主要不同点是,MSCS是设计成保护群集应用的状态,而HA被设计成保护虚拟机,无论虚拟机上是什么类型的应用,但也可以通过虚拟机和应用程序的监控扩展到应用层。 在有HA的情况下,故障转移发生的停机时间就是虚拟机在群集中另一节点上虚拟机重新启动的时间,而MSCS故障转移发生的停机时间为服务切换到群集中另一节点的时间,与很多人认为的观点形成对比的是,MSCS不保证在群集故障转移时是零停机时间,另外,为了更有效的利用这一机制,你们的应用需要开启群集状态监测。 可能有这样一个问题,当虚拟机重新启动,应用服务临时真空的情况下,为什么要用到HA,这个问题很简单,不是所有的虚拟机或者应用服务需要99.999%时间开机提供服务,HA能够满足足够多服务的需求,另外,许多应用不支持MSCS群集故障转移功能,这意味着如果群集中的应用没有被MSCS检测到时,不能保证应用的高可用性和数据一致性。 另外,MSCS群集配置比较复杂,需要专业的技术和背景,打个比方,补丁管理服务器和更新服务器在一个MSCS环境中,如果配置不正确会导致更多的故障停机时间及更复杂的程序配置方式。但HA可以减少复杂度,开销时间,资源消耗和停机时间,这些可以体现HA的重要性,相反的是MSCS,不需要任何改变的guest,HA仍可以在虚拟化层提供服务,而且,虚拟机监测除了安装VMwareTools不需要其它的附加软件或者系统模块,无论如何,安装VMwareTools是一个非常好的方法。如果需要更高基本的高可用性,VMware还可以通过应用程序监控提供,和赛门铁克一样被利用来弹性定义应用级别,还可以在内部团队进行开发时,增加应用的弹性。 HA已经一次又一次的证明自己,它已经广泛的应用于各个领域,如果你到今天为止还没有使用它,看完这部书希望你能相信。 vSphere5.1 当我们深入HA的主体架构中,并描述怎样配置使用HA时,我们首先简单的介绍下vSphere5.0和5.1的变化,描述启用HA的基本要求和步骤,聚焦这边书,可以增强虚拟架构中的稳定性,我们还认识到,你们有可能还不熟悉vSphere5.0,我们同样会强调vSphere5.0的变化,我们在适当的情况下,仍然会讨论功能和概念,包括自从vCenter2.0之后出现组件,以确保您在各个方面更全面的理解HA。 5.X的变化 在vSphere5.0之前,HA的功能没有得到大的增强,也许你在vSphere5.0和vSphere5.1之间看不到变化,但他们有些地方还是很突出。 在vSphere5.0的HA完全从底层被重新设计和开发,这也是HA从之前故障转移需要分钟为单位代替为秒为单位的原因 vSphere5.0中重新设计的HA,许多非常受欢迎的改变可以拿来介绍HA的扩展性,一些HA的关键组件完全被更改,同时添加了新的功能,为了便利,我们把改变列了下面的清单,那样我们可以更详细的讨论这个章节,vSphere5.0的所有改变如下表所示: 新的HA的代理——FDM是新的HA代理,HA已经从最底层重新设计,也就是说FDM替换了AAM代理 不依赖DNS——为了避免依赖DNS,HA只需要写入IP即可。 主节点的概念——主/备节点的数量限制被取消(vSphere4.1版本以前最多支持5个主节点) 支持管理网络分区——当存在多个网络分区时,允许有多个主节点 增强隔离验证——当管理网络出现故障时,避免误报 数据存储心跳检测——在通过存储层验证主机状态时,这种额外的信号检测可以减少误报的机会, 增强管理控制规则 1台主机故障允许4台主机接管(最大支持31台) 以百分比为控制粒度,可以允许你单独规定CPU或者内存的使用率 基于接入控制策略,可以允许多个主机成为故障主机的目标重启主机 vSphere5.1则增加了一些功能,虽然与vSphere5.0相比,这些似乎是次要的,他们是非常重要的了解,以便创建一个高度灵活的环境。 通过vSphereWebClient设置主机故障接纳值 能够检索跨越多个插槽的虚拟机的列表 增强了设备永久丢失的处理情况(vSphere5.0.1有介绍) 支持虚拟机系统的休眠模式 GuestSDK中包括应用监测SDK vSphereHA(FDM)VIB自动被添加到自动部署步骤中 通过”das.config.fdm.iso-lationPolicyDelaySec”设置延迟响应 HA工作需要哪些条件 每个产品功能都需要相关的前提条件支持,HA也不例外,了解HA的前提条件也是学习的基础部分,我们必须抛开之前我们提到的复杂的概念,来迎接全新的HA,我还要像您展示怎样配置HA。 先决条件 在配置HA前,强烈要求在环境中验证它的先决条件,为增加弹性,我们还建议在基础环境中也验证先决条件。 需求: 最少2台ESXi主机 安装ESXi的主机至少3GB内存 VMwarevCenterServer 虚拟机能访问的共享存储 可靠的网关或者其它IP地址 建议: 管理网络冗余(不是必须的,但强烈建议) 多路径共享存储 防火墙需求 在下表中是HA需要用于通信的端口,如果主机的内网环境运行着防火墙设备,为保证HA功能正常这些端口必须打开,在ESX或者ESXi防火墙上也同样需要打开以下端口,需要注意的地方是vSphere5.0之前需要多个端口来进行HA通信,而vSphere只需要用到一个端口。 表一:高可用端口设置 配置vSphere的高可用性 HA只需要点击几次即可完成配置,接下来的步骤将会展示怎样建立群集和开启HA,包括虚拟机监控,使用vSphereWebClient,这些设置和设计在接下来的章节内容中详细描述。 1.登录连接vCenterServer 2.数据中心右键建立新的群集 3.给新的群集命名,建议名称中至少包含位置信息,然后按序号排列,如ie.ams-hadrs-001 4.选择开启HA 5.启用主机监控和策略 6.在接入控制策略下选择作为故障切换空间容量保留的群集资源的百分比 7.开启”虚拟机和应用程序监控“ 8.点击“OK”完成配置 图3:群集即将完成配置信息 当HA群集建立完成,右键点击ESXi主机可以迁移它到群集中,如果加入了vCenter,在群集上右键添加ESXi主机即可 当一台ESXi主机首次添加到新建的群集中,HA的代理会被加载和配置,一旦代理配置完成,HA将会为运行中的ESXi主机提供保护。 正如我们所阐述的,HA是一个简单的群集解决方案,它可以在主机或者操作系统出现故障的几分钟内保证虚拟机继续工作,了解了HA的架构,当涉及到可用性时,我们能更容易解决问题,在接下来的章节,我们将讨论HA的架构和基本概念,我们还将讨论在客户的环境中我们如何根据需求来决策配置HA的方式。 本文转自 tim2009 51CTO博客,原文链接:http://blog.51cto.com/virtualbox/1168293,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android RecyclerView (一) 使用完全解析

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/45059587; 本文出自:【张鸿洋的博客】 概述 RecyclerView出现已经有一段时间了,相信大家肯定不陌生了,大家可以通过导入support-v7对其进行使用。 据官方的介绍,该控件用于在有限的窗口中展示大量数据集,其实这样功能的控件我们并不陌生,例如:ListView、GridView。 那么有了ListView、GridView为什么还需要RecyclerView这样的控件呢?整体上看RecyclerView架构,提供了一种插拔式的体验,高度的解耦,异常的灵活,通过设置它提供的不同LayoutManager,ItemDecoration , ItemAnimator实现令人瞠目的效果。 你想要控制其显示的方式,请通过布局管理器LayoutManager 你想要控制Item间的间隔(可绘制),请通过ItemDecoration 你想要控制Item增删的动画,请通过ItemAnimator 你想要控制点击、长按事件,请自己写(擦,这点尼玛。) 基本使用 鉴于我们对于ListView的使用特别的熟悉,对比下RecyclerView的使用代码: mRecyclerView = findView(R.id.id_recyclerview); //设置布局管理器 mRecyclerView.setLayoutManager(layout); //设置adapter mRecyclerView.setAdapter(adapter) //设置Item增加、移除动画 mRecyclerView.setItemAnimator(new DefaultItemAnimator()); //添加分割线 mRecyclerView.addItemDecoration(new DividerItemDecoration( getActivity(), DividerItemDecoration.HORIZONTAL_LIST)); ok,相比较于ListView的代码,ListView可能只需要去设置一个adapter就能正常使用了。而RecyclerView基本需要上面一系列的步骤,那么为什么会添加这么多的步骤呢? 那么就必须解释下RecyclerView的这个名字了,从它类名上看,RecyclerView代表的意义是,我只管Recycler View,也就是说RecyclerView只管回收与复用View,其他的你可以自己去设置。可以看出其高度的解耦,给予你充分的定制自由(所以你才可以轻松的通过这个控件实现ListView,GirdView,瀑布流等效果)。 Just like ListView Activity package com.zhy.sample.demo_recyclerview; import java.util.ArrayList; import java.util.List; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.ViewHolder; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; public class HomeActivity extends ActionBarActivity { private RecyclerView mRecyclerView; private List<String> mDatas; private HomeAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_single_recyclerview); initData(); mRecyclerView = (RecyclerView) findViewById(R.id.id_recyclerview); mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); mRecyclerView.setAdapter(mAdapter = new HomeAdapter()); } protected void initData() { mDatas = new ArrayList<String>(); for (int i = 'A'; i < 'z'; i++) { mDatas.add("" + (char) i); } } class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder> { @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { MyViewHolder holder = new MyViewHolder(LayoutInflater.from( HomeActivity.this).inflate(R.layout.item_home, parent, false)); return holder; } @Override public void onBindViewHolder(MyViewHolder holder, int position) { holder.tv.setText(mDatas.get(position)); } @Override public int getItemCount() { return mDatas.size(); } class MyViewHolder extends ViewHolder { TextView tv; public MyViewHolder(View view) { super(view); tv = (TextView) view.findViewById(R.id.id_num); } } } Activity的布局文件 <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.support.v7.widget.RecyclerView android:id="@+id/id_recyclerview" android:divider="#ffff0000" android:dividerHeight="10dp" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout> Item的布局文件 <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:background="#44ff0000" android:layout_height="wrap_content" > <TextView android:id="@+id/id_num" android:layout_width="match_parent" android:layout_height="50dp" android:gravity="center" android:text="1" /> </FrameLayout> 这么看起来用法与ListView的代码基本一致哈~~ 看下效果图: 看起来好丑,Item间应该有个分割线,当你去找时,你会发现RecyclerView并没有支持divider这样的属性。那么怎么办,你可以给Item的布局去设置margin,当然了这种方式不够优雅,我们文章开始说了,我们可以自由的去定制它,当然我们的分割线也是可以定制的。 ItemDecoration 我们可以通过该方法添加分割线:mRecyclerView.addItemDecoration() 该方法的参数为RecyclerView.ItemDecoration,该类为抽象类,官方目前并没有提供默认的实现类(我觉得最好能提供几个)。 该类的源码: public static abstract class ItemDecoration { public void onDraw(Canvas c, RecyclerView parent, State state) { onDraw(c, parent); } public void onDrawOver(Canvas c, RecyclerView parent, State state) { onDrawOver(c, parent); } public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) { getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(), parent); } @Deprecated public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { outRect.set(0, 0, 0, 0); } 当我们调用mRecyclerView.addItemDecoration()方法添加decoration的时候,RecyclerView在绘制的时候,去会绘制decorator,即调用该类的onDraw和onDrawOver方法, onDraw方法先于drawChildren onDrawOver在drawChildren之后,一般我们选择复写其中一个即可。 getItemOffsets 可以通过outRect.set()为每个Item设置一定的偏移量,主要用于绘制Decorator。 接下来我们看一个RecyclerView.ItemDecoration的实现类,该类很好的实现了RecyclerView添加分割线(当使用LayoutManager为LinearLayoutManager时)。 该类参考自:DividerItemDecoration package com.zhy.sample.demo_recyclerview; /* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * limitations under the License. */ import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.State; import android.util.Log; import android.view.View; /** * This class is from the v7 samples of the Android SDK. It's not by me! * <p/> * See the license above for details. */ public class DividerItemDecoration extends RecyclerView.ItemDecoration { private static final int[] ATTRS = new int[]{ android.R.attr.listDivider }; public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL; public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL; private Drawable mDivider; private int mOrientation; public DividerItemDecoration(Context context, int orientation) { final TypedArray a = context.obtainStyledAttributes(ATTRS); mDivider = a.getDrawable(0); a.recycle(); setOrientation(orientation); } public void setOrientation(int orientation) { if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) { throw new IllegalArgumentException("invalid orientation"); } mOrientation = orientation; } @Override public void onDraw(Canvas c, RecyclerView parent) { Log.v("recyclerview - itemdecoration", "onDraw()"); if (mOrientation == VERTICAL_LIST) { drawVertical(c, parent); } else { drawHorizontal(c, parent); } } public void drawVertical(Canvas c, RecyclerView parent) { final int left = parent.getPaddingLeft(); final int right = parent.getWidth() - parent.getPaddingRight(); final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(parent.getContext()); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int top = child.getBottom() + params.bottomMargin; final int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } public void drawHorizontal(Canvas c, RecyclerView parent) { final int top = parent.getPaddingTop(); final int bottom = parent.getHeight() - parent.getPaddingBottom(); final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int left = child.getRight() + params.rightMargin; final int right = left + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } @Override public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { if (mOrientation == VERTICAL_LIST) { outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); } else { outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); } } } 该实现类可以看到通过读取系统主题中的android.R.attr.listDivider作为Item间的分割线,并且支持横向和纵向。如果你不清楚它是怎么做到的读取系统的属性用于自身,请参考我的另一篇博文:Android 深入理解Android中的自定义属性 获取到listDivider以后,该属性的值是个Drawable,在getItemOffsets中,outRect去设置了绘制的范围。onDraw中实现了真正的绘制。 我们在原来的代码中添加一句: mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST)); ok,现在再运行,就可以看到分割线的效果了。 该分割线是系统默认的,你可以在theme.xml中找到该属性的使用情况。那么,使用系统的listDivider有什么好处呢?就是方便我们去随意的改变,该属性我们可以直接声明在: <!-- Application theme. --> <style name="AppTheme" parent="AppBaseTheme"> <item name="android:listDivider">@drawable/divider_bg</item> </style> 然后自己写个drawable即可,下面我们换一种分隔符: <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > <gradient android:centerColor="#ff00ff00" android:endColor="#ff0000ff" android:startColor="#ffff0000" android:type="linear" /> <size android:height="4dp"/> </shape> 现在的样子是: 当然了,你可以根据自己的需求,去随意的绘制,反正是画出来的,随便玩~~ ok,看到这,你可能觉得,这玩意真尼玛麻烦,完全不能比拟的心爱的ListView。那么继续看。 LayoutManager 好了,上面实现了类似ListView样子的Demo,通过使用其默认的LinearLayoutManager。 RecyclerView.LayoutManager吧,这是一个抽象类,好在系统提供了3个实现类: LinearLayoutManager 现行管理器,支持横向、纵向。 GridLayoutManager 网格布局管理器 StaggeredGridLayoutManager 瀑布就式布局管理器 上面我们已经初步体验了下LinearLayoutManager,接下来看GridLayoutManager。 GridLayoutManager 我们尝试去实现类似GridView,秒秒钟的事情: //mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); mRecyclerView.setLayoutManager(new GridLayoutManager(this,4)); 只需要修改LayoutManager即可,还是很nice的。 当然了,改为GridLayoutManager以后,对于分割线,前面的DividerItemDecoration就不适用了,主要是因为它在绘制的时候,比如水平线,针对每个child的取值为: final int left = parent.getPaddingLeft(); final int right = parent.getWidth() - parent.getPaddingRight(); 因为每个Item一行,这样是没问题的。而GridLayoutManager时,一行有多个childItem,这样就多次绘制了,并且GridLayoutManager时,Item如果为最后一列(则右边无间隔线)或者为最后一行(底部无分割线)。 针对上述,我们编写了DividerGridItemDecoration。 package com.zhy.sample.demo_recyclerview; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.LayoutManager; import android.support.v7.widget.RecyclerView.State; import android.support.v7.widget.StaggeredGridLayoutManager; import android.view.View; /** * * @author zhy * */ public class DividerGridItemDecoration extends RecyclerView.ItemDecoration { private static final int[] ATTRS = new int[] { android.R.attr.listDivider }; private Drawable mDivider; public DividerGridItemDecoration(Context context) { final TypedArray a = context.obtainStyledAttributes(ATTRS); mDivider = a.getDrawable(0); a.recycle(); } @Override public void onDraw(Canvas c, RecyclerView parent, State state) { drawHorizontal(c, parent); drawVertical(c, parent); } private int getSpanCount(RecyclerView parent) { // 列数 int spanCount = -1; LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { spanCount = ((GridLayoutManager) layoutManager).getSpanCount(); } else if (layoutManager instanceof StaggeredGridLayoutManager) { spanCount = ((StaggeredGridLayoutManager) layoutManager) .getSpanCount(); } return spanCount; } public void drawHorizontal(Canvas c, RecyclerView parent) { int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int left = child.getLeft() - params.leftMargin; final int right = child.getRight() + params.rightMargin + mDivider.getIntrinsicWidth(); final int top = child.getBottom() + params.bottomMargin; final int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } public void drawVertical(Canvas c, RecyclerView parent) { final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int top = child.getTop() - params.topMargin; final int bottom = child.getBottom() + params.bottomMargin; final int left = child.getRight() + params.rightMargin; final int right = left + mDivider.getIntrinsicWidth(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } private boolean isLastColum(RecyclerView parent, int pos, int spanCount, int childCount) { LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边 { return true; } } else if (layoutManager instanceof StaggeredGridLayoutManager) { int orientation = ((StaggeredGridLayoutManager) layoutManager) .getOrientation(); if (orientation == StaggeredGridLayoutManager.VERTICAL) { if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边 { return true; } } else { childCount = childCount - childCount % spanCount; if (pos >= childCount)// 如果是最后一列,则不需要绘制右边 return true; } } return false; } private boolean isLastRaw(RecyclerView parent, int pos, int spanCount, int childCount) { LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { childCount = childCount - childCount % spanCount; if (pos >= childCount)// 如果是最后一行,则不需要绘制底部 return true; } else if (layoutManager instanceof StaggeredGridLayoutManager) { int orientation = ((StaggeredGridLayoutManager) layoutManager) .getOrientation(); // StaggeredGridLayoutManager 且纵向滚动 if (orientation == StaggeredGridLayoutManager.VERTICAL) { childCount = childCount - childCount % spanCount; // 如果是最后一行,则不需要绘制底部 if (pos >= childCount) return true; } else // StaggeredGridLayoutManager 且横向滚动 { // 如果是最后一行,则不需要绘制底部 if ((pos + 1) % spanCount == 0) { return true; } } } return false; } @Override public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { int spanCount = getSpanCount(parent); int childCount = parent.getAdapter().getItemCount(); if (isLastRaw(parent, itemPosition, spanCount, childCount))// 如果是最后一行,则不需要绘制底部 { outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); } else if (isLastColum(parent, itemPosition, spanCount, childCount))// 如果是最后一列,则不需要绘制右边 { outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); } else { outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight()); } } 主要在getItemOffsets方法中,去判断如果是最后一行,则不需要绘制底部;如果是最后一列,则不需要绘制右边,整个判断也考虑到了StaggeredGridLayoutManager的横向和纵向,所以稍稍有些复杂。最重要还是去理解,如何绘制什么的不重要。一般如果仅仅是希望有空隙,还是去设置item的margin方便。 最后的效果是: ok,看到这,你可能还觉得RecyclerView不够强大? 但是如果我们有这么个需求,纵屏的时候显示为ListView,横屏的时候显示两列的GridView,我们RecyclerView可以轻松搞定,而如果使用ListView去实现还是需要点功夫的~~~ 当然了,这只是皮毛,下面让你心服口服。 StaggeredGridLayoutManager 瀑布流式的布局,其实他可以实现GridLayoutManager一样的功能,仅仅按照下列代码: // mRecyclerView.setLayoutManager(new GridLayoutManager(this,4)); mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(4, StaggeredGridLayoutManager.VERTICAL)); 这两种写法显示的效果是一致的,但是注意StaggeredGridLayoutManager构造的第二个参数传一个orientation,如果传入的是StaggeredGridLayoutManager.VERTICAL代表有多少列;那么传入的如果是StaggeredGridLayoutManager.HORIZONTAL就代表有多少行,比如本例如果改为: mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(4, StaggeredGridLayoutManager.HORIZONTAL)); 那么效果为: 可以看到,固定为4行,变成了左右滑动。有一点需要注意,如果是横向的时候,item的宽度需要注意去设置,毕竟横向的宽度没有约束了,应为控件可以横向滚动了。 如果你需要一样横向滚动的GridView,那么恭喜你。 ok,接下来准备看大招,如果让你去实现个瀑布流,最起码不是那么随意就可以实现的吧?但是,如果使用RecyclerView,分分钟的事。 那么如何实现?其实你什么都不用做,只要使用StaggeredGridLayoutManager我们就已经实现了,只是上面的item布局我们使用了固定的高度,下面我们仅仅在适配器的onBindViewHolder方法中为我们的item设置个随机的高度(代码就不贴了,最后会给出源码下载地址),看看效果图: 是不是棒棒哒,通过RecyclerView去实现ListView、GridView、瀑布流的效果基本上没有什么区别,而且可以仅仅通过设置不同的LayoutManager即可实现。 还有更nice的地方,就在于item增加、删除的动画也是可配置的。接下来看一下ItemAnimator。 ItemAnimator ItemAnimator也是一个抽象类,好在系统为我们提供了一种默认的实现类,期待系统多 添加些默认的实现。 借助默认的实现,当Item添加和移除的时候,添加动画效果很简单: // 设置item动画 mRecyclerView.setItemAnimator(new DefaultItemAnimator()); 系统为我们提供了一个默认的实现,我们为我们的瀑布流添加以上一行代码,效果为: 如果是GridLayoutManager呢?动画效果为: 注意,这里更新数据集不是用adapter.notifyDataSetChanged()而是notifyItemInserted(position)与notifyItemRemoved(position) 否则没有动画效果。 上述为adapter中添加了两个方法: public void addData(int position) { mDatas.add(position, "Insert One"); notifyItemInserted(position); } public void removeData(int position) { mDatas.remove(position); notifyItemRemoved(position); } Activity中点击MenuItem触发: @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.id_action_add: mAdapter.addData(1); break; case R.id.id_action_delete: mAdapter.removeData(1); break; } return true; } 好了,到这我对这个控件已经不是一般的喜欢了~~~ 当然了只提供了一种动画,那么我们肯定可以去自定义各种nice的动画效果。 高兴的是,github上已经有很多类似的项目了,这里我们直接引用下:RecyclerViewItemAnimators,大家自己下载查看。 提供了SlideInOutLeftItemAnimator,SlideInOutRightItemAnimator,SlideInOutTopItemAnimator,SlideInOutBottomItemAnimator等动画效果。 Click and LongClick 不过一个挺郁闷的地方就是,系统没有提供ClickListener和LongClickListener。 不过我们也可以自己去添加,只是会多了些代码而已。 实现的方式比较多,你可以通过mRecyclerView.addOnItemTouchListener去监听然后去判断手势, 当然你也可以通过adapter中自己去提供回调,这里我们选择后者,前者的方式,大家有兴趣自己去实现。 那么代码也比较简单: class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder> { //... public interface OnItemClickLitener { void onItemClick(View view, int position); void onItemLongClick(View view , int position); } private OnItemClickLitener mOnItemClickLitener; public void setOnItemClickLitener(OnItemClickLitener mOnItemClickLitener) { this.mOnItemClickLitener = mOnItemClickLitener; } @Override public void onBindViewHolder(final MyViewHolder holder, final int position) { holder.tv.setText(mDatas.get(position)); // 如果设置了回调,则设置点击事件 if (mOnItemClickLitener != null) { holder.itemView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { int pos = holder.getLayoutPosition(); mOnItemClickLitener.onItemClick(holder.itemView, pos); } }); holder.itemView.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { int pos = holder.getLayoutPosition(); mOnItemClickLitener.onItemLongClick(holder.itemView, pos); return false; } }); } } //... } adapter中自己定义了个接口,然后在onBindViewHolder中去为holder.itemView去设置相应 的监听最后回调我们设置的监听。 最后别忘了给item添加一个drawable: <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android" > <item android:state_pressed="true" android:drawable="@color/color_item_press"></item> <item android:drawable="@color/color_item_normal"></item> </selector> Activity中去设置监听: mAdapter.setOnItemClickLitener(new OnItemClickLitener() { @Override public void onItemClick(View view, int position) { Toast.makeText(HomeActivity.this, position + " click", Toast.LENGTH_SHORT).show(); } @Override public void onItemLongClick(View view, int position) { Toast.makeText(HomeActivity.this, position + " long click", Toast.LENGTH_SHORT).show(); mAdapter.removeData(position); } }); 测试效果: ok,到此我们基本介绍了RecylerView常见用法,包含了: 系统提供了几种LayoutManager的使用; 如何通过自定义ItemDecoration去设置分割线,或者一些你想作为分隔的drawable,注意这里 巧妙的使用了系统的listDivider属性,你可以尝试添加使用divider和dividerHeight属性。 如何使用ItemAnimator为RecylerView去添加Item移除、添加的动画效果。 介绍了如何添加ItemClickListener与ItemLongClickListener。 可以看到RecyclerView可以实现: ListView的功能 GridView的功能 横向ListView的功能,参考Android 自定义RecyclerView 实现真正的Gallery效果 横向ScrollView的功能 瀑布流效果 便于添加Item增加和移除动画 整个体验下来,感觉这种插拔式的设计太棒了,如果系统再能提供一些常用的分隔符,多添加些动画效果就更好了。 通过简单改变下LayoutManager,就可以产生不同的效果,那么我们可以根据手机屏幕的宽度去动态设置LayoutManager,屏幕宽度一般的,显示为ListView;宽度稍大的显示两列的GridView或者瀑布流(或者横纵屏幕切换时变化,有点意思~);显示的列数和宽度成正比。甚至某些特殊屏幕,让其横向滑动~~再选择一个nice的动画效果,相信这种插件式的编码体验一定会让你迅速爱上RecyclerView。 参考资料 Android 自定义RecyclerView 实现真正的Gallery效果 本文转自 一点点征服 博客园博客,原文链接:http://www.cnblogs.com/ldq2016/p/5377225.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android内部存储与外部存储解析

Android开发的过程中。经常会涉及到存储,之前一直没有一个整体的概念,这篇文章就是进行一个知识点的梳理。 Android的存储有:内部存储、外部存储。 想要了解这两个概念,我们先将开发软件中的DDMS打开,看File Explorer,如下: 1、内部存储 上边的data文件夹,就是内部存储。data文件夹中有两个重要的文件夹app、data。 app文件夹存放着我们所有已安装的软件的apk文件。 data文件夹存放以包名命名的文件夹。每个文件夹里是: /data/data/包名/shared_prefs /data/data/包名/databases /data/data/包名/files /data/data/包名/cache 这四个文件夹,其中SharePreferenced的数据就是存在shared_prefs中。软件中的数据库文件就是存在databases中,普通文件存在files中,缓存存在cache中。 在内部存储中的数据,当相关的软件删除时,会被同时删除。 2、外部存储 图中的mnt文件夹就是外部存储,不同的手机生产厂商对应的文件夹还可能是storage文件夹。在storage文件夹中还有一个文件夹sdcard。这个就是我们平时调用获取外部存储api时获取文件夹。在这个文件夹中公有目录和私有目录。公有目录有九大类。私有目录只有Android这一个,里边也是以包名为目录的。 在很多文章中说,内部存储就是手机自带的内存、手机出厂时已经有的内存。外部存储就是通过扩展SD卡的功能后增加的存储卡,这种是不正确的。 早期的Android设备,的确内部存储时自带的、固定的。但是后来的手机将自身的存储扩展到了8G以及更高,许多手机也不再提供扩展SD卡功能。这时就只是在概念上把手机的存储划出了内部存储和外部存储。所以不管有没有SD卡,都有内部存储和外部存储。但是不管什么情况,访问内部存储和外部存储都用的同一套Android Api。 在外部存储中的数据,当卸载软件时,该软件存在私有目录/data/storage/Android/data/包名/ 下的数据将全部删掉。软件存在公有目录下的数据不会被删掉。 3、存储空间的操作 操作内部存储空间和外部存储空间的私有目录时,以context获取的。操作外部存储空间时,以Environmnet获取。方法总结如下: 内部存储 目录 方法 /data/data/包名/files context.getFilesDir() /data/data/包名/cache context.getCacheDir() 外部存储 目录 方法 根目录 Environment.getExternalStorageDirectory() 公有目录(九大公有目录) Environment.getExternalStoragePublicDirectory(String type) 私有目录/data/storage/Android/data/包名/files context.getExternalFilesDir(String type) 私有目录/data/storage/Android/data/包名/cache context.getExternalCacheDir() 其中,公有目录的type类型,我们查看源码: /** * Get a top-level shared/external storage directory for placing files of a * particular type. This is where the user will typically place and manage * their own files, so you should be careful about what you put here to * ensure you don't erase their files or get in the way of their own * organization. * <p> * On devices with multiple users (as described by {@link UserManager}), * each user has their own isolated shared storage. Applications only have * access to the shared storage for the user they're running as. * </p> * <p> * Here is an example of typical code to manipulate a picture on the public * shared storage: * </p> * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java * public_picture} * * @param type The type of storage directory to return. Should be one of * {@link #DIRECTORY_MUSIC}, {@link #DIRECTORY_PODCASTS}, * {@link #DIRECTORY_RINGTONES}, {@link #DIRECTORY_ALARMS}, * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_PICTURES}, * {@link #DIRECTORY_MOVIES}, {@link #DIRECTORY_DOWNLOADS}, * {@link #DIRECTORY_DCIM}, or {@link #DIRECTORY_DOCUMENTS}. May not be null. * @return Returns the File path for the directory. Note that this directory * may not yet exist, so you must make sure it exists before using * it such as with {@link File#mkdirs File.mkdirs()}. */ public static File getExternalStoragePublicDirectory(String type) { throwIfUserRequired(); return sCurrentUser.buildExternalStoragePublicDirs(type)[0]; } 可以看出九种类型是: 类型 意义 Environment.DIRECTORY_MUSIC 音乐保存的位置 Environment.DIRECTORY_PODCASTS 用于保存podcast(博客)的音频文件 Environment.DIRECTORY_RINGTONES 保存铃声的位置 Environment.DIRECTORY_ALARMS 警报的铃声 Environment.DIRECTORY_NOTIFICATIONS 通知音保存的位置 Environment.DIRECTORY_PICTURES 下载的图片保存的位置 Environment.DIRECTORY_MOVIES 电影保存的位置 Environment.DIRECTORY_DOWNLOADS 下载文件保存的位置 Environment.DIRECTORY_DCIM 相机拍摄的图片和视频保存的位置 其中,context.getExternalFilesDir(String type)的类型有7种,看主要源码: /** * @param type The type of files directory to return. May be {@code null} * for the root of the files directory or one of the following * constants for a subdirectory: * {@link android.os.Environment#DIRECTORY_MUSIC}, * {@link android.os.Environment#DIRECTORY_PODCASTS}, * {@link android.os.Environment#DIRECTORY_RINGTONES}, * {@link android.os.Environment#DIRECTORY_ALARMS}, * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS}, * {@link android.os.Environment#DIRECTORY_PICTURES}, or * {@link android.os.Environment#DIRECTORY_MOVIES}. * @return the absolute path to application-specific directory. May return * {@code null} if shared storage is not currently available. * @see #getFilesDir * @see #getExternalFilesDirs(String) * @see Environment#getExternalStorageState(File) * @see Environment#isExternalStorageEmulated(File) * @see Environment#isExternalStorageRemovable(File) */ @Nullable public abstract File getExternalFilesDir(@Nullable String type); 可以看出七种类型是: 类型 意义 Environment.DIRECTORY_MUSIC 音乐保存的位置 Environment.DIRECTORY_PODCASTS 用于保存podcast(博客)的音频文件 Environment.DIRECTORY_RINGTONES 保存铃声的位置 Environment.DIRECTORY_ALARMS 警报的铃声 Environment.DIRECTORY_NOTIFICATIONS 通知音保存的位置 Environment.DIRECTORY_PICTURES 下载的图片保存的位置 Environment.DIRECTORY_MOVIES 电影保存的位置 注意:这个方法是可以传一个null的参数的,表示获取的是当前目录。如果传入类型,则获取的是对应的子目录。 下边给出一个对上边讲述方法进行运用的一个工具类: import android.content.Context; import android.os.Environment; public class SDUtil { // 判断SD卡是否被挂载 public static boolean isSDCardMounted() { return Environment.MEDIA_MOUNTED.equals( Environment.getExternalStorageState()); } // 获取内部存储文件的存储路径 public static String getFilePath(Context context) { return context.getFilesDir().getPath(); } // 获取内部存储缓存的存储路径 public static String getCachePath(Context context) { return context.getCacheDir().getPath(); } // 获取外部存储根的存储路径,因为不同的商家外部存储的绝对路径可能不同,所以用相对路径进行操作 public static String getExternalPath() { if (isSDCardMounted()){ return Environment.getExternalStorageDirectory().getAbsolutePath(); } return null; } // 获取外部存储公共目录的存储路径 public static String getExternalPublicPath(String type) { if (isSDCardMounted()){ return Environment.getExternalStoragePublicDirectory(type).getAbsolutePath(); } return null; } // 获取外部存储私有目录的文件的存储路径 public static String getExternalFilePath(Context context,String type) { if (isSDCardMounted()){ return context.getExternalFilesDir(type).getAbsolutePath(); } return null; } // 获取外部存储私有目录的缓存的存储路径 public static String getExternalCachePath(Context context,String type){ if (isSDCardMounted()){ return context.getExternalCacheDir().getAbsolutePath(); } return null; } } 这篇博客是一篇学习总结,如有错误的地方,请大家指正。

优秀的个人博客,低调大师

WiFi行业9大趋势解析

有鉴于近期Wi-Fi创新消息不断出现,预计2016年无线LAN技术发展将出现10项趋势值得外界关注,例如第二波802.11ac问世、分析技术成为必备条件、2.5/5/10Gbps出现以及Wi-Fi与LTE冲突持续、与更多Wi-Fi会在物联网(IoT)上采用。 据TechTarget网站报导,在十种趋势中,首先是第二波802.11ac正式报到。第二波802.11ac通常是指符合下列三项定义其中之一。 第一是可支援3种以上MIMO串流,而且其中2~3项为外界熟知,第二则是频宽最高可达160MHz,第三则是可支援多用户MIMO功能。 评论认为,虽然上述所有功能在2015年底前仍未全部到位,但由于预期无线射频基础建设、设计、韧体、天线与管理软体都会陆续取得进展,代表第二波802.11ac也将在今年成为新主流。 第二则是分析(Analytics)将成为必备功能。分析是指与采用的巨量数据有关的技术,以无线LAN技术为例,届时产生出与效能最大化、安全、应用程式使用等数据量相当庞大,因此须仰赖分析工具。 第三则是2.5/5/10Gbps将取代1Gbps网路聚合(link aggregation)。随着第二波802.11ac出现后,代表1GbE时代将退场。不过,目前仍有讨论空间的则是2.5/5Gbps产品是否会取代目前的2Gbps网路聚合,或下一波升级会直接达到10Gbps。 第四则是云端管理功能增多。由于成本下降、稳定性提升、取得容易以及可透过手机掌握全球性业务成真后,云端网路管理已逐渐被外界熟知与运用。加上拜软体定义网路(SDN、网路即服务(networking-as-a service)、稳定性及容错提升所赐,预计今年将看见更多网路功能开始移植至云端上。 第五则是802.11ad/WiGig正式报到。随着许多产品开始部署后,外界对于802.11ad或Wi-Fi联盟(Wi-Fi Alliance)称为的WiGig错误想法,也将告一段落。届时外界不管在开放办公室或密闭空间内,都将见识到802.11ad庞大的资料传输流通量(Throughput)以及效能惊人表现。 第六则是Wi-Fi将主宰物联网市场,因为Wi-Fi将成为许多商用或消费者应用的无线首选技术,而且目前市场上已出现各种规格、价格、效能、电池寿命与成本的产品与元件。 第七则是Passpoint与Wi-Fi Aware将简化用户移动装置的使用。在Wi-Fi联盟推出Passpoint技术后,让基地台系统连接Wi-Fi网路无须透过点击,等于宣告过去必须点击SSID与输入密码来连接Wi-Fi网路做法将成为历史,而Wi-Fi Aware功能提供服务探测也会提高其便利性。 第八则是Wi-Fi与未授权LTE(unlicensed LTE)冲突将会持续。因为少数基地台业者在Wi-Fi使用的频谱上部署未授权版本LTE将在今年成为趋势。虽然该频谱并非Wi-Fi独有,不过,由于Wi-Fi目前已到处可见并在部份设定成为关键任务,加上目前未授权LTE版本会干扰Wi-Fi运作的结果,代表外界势必得想出对策。 第九则是Wi-Fi将更为常见,例如在汽车、飞机、家庭、职场或其他地方都将见到Wi-Fi踪影,甚至微软(Microsoft)与Google等企业也会推出公共存取Wi-Fi服务。 最后则是无线LAN在过去20年来一直创新后将会出现更多成果。例如国际电机电子工程师学会(IEEE)目前已部署新标准,包括5GHz频带吞吐量预计可达10Gbps的802.11ax以及60GHz可达20Gbps的802.11ay标准,都将为提升处理量再创下新的标竿。 本文转自d1net(转载)

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册