zoukankan      html  css  js  c++  java
  • 中级实训Android学习记录——Activity、Fragment

    学习记录 2020/11/24

    Activity

    • 创建一个Activity所需的三个步骤
    1. 创建一个继承Activity的类

      • 这个Activity可以是很多不同的类,如可以继承AppCompatActivity
      public class TestActivity extends AppCompatActivity {}
      
    2. 把创建好的Activity的类在AndroidManifest中声明

      // 在AndroidManifest.xml中
      <activity android:name=".TestActivity"></activity>
      
    3. 为创建好的Activity创建layout并在Activity的onCreate中设置

      @override
      protected void onCreate(...){
      	super.onCreate(...);
      	setContentView(R.layout.activity_test);
      }
      

    Note:在Android中直接创建Activity会直接帮你做好以上三个步骤

    • AndroidManifest.xml

    // 导航栏ActionBar的设置

    // 在Activity中设置

    <activity ... android:label="Test"/> // 设置导航栏的标题为Test

    <activity ... android:theme="@style/Theme.AppCompat.Light.NoActionBar"/> // 设置该activity不显示默认导航栏

    <application ... android:theme="@style/Theme.AppCompat.Light.NoActionBar"/> // 设置整个应用的activity都不显示默认导航栏

    // 设置屏幕方向

    <activity ... android:screenOrientation="portrait"/> 竖屏锁定

    <activity ... android:screenOrientation="landscape"/> 横屏锁定

    // 设置默认启动的activity

    // 将以下代码加入activity中即可将选定的activity设置成默认启动的activity

    <intent-filter>
    	<action android:name="android.intent.action.MAIN"/>
     <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
    
    • Activity的生命周期

    调用顺序

    OnCreate()
    OnStart()
    OnResume()
    OnPuase()
    OnStop()
    OnDestroy()
    
    • Activity的跳转和数据传递

      • 跳转的各种方式
        • 显式跳转和隐式跳转
      // 假设我们目前在AActivity中,想要跳转到BActivity中去
      // 显式1(最常用)
      	Intent intent = new Intent(AActivity.this, BActivity.class);
      	startActivity(intent);
      // 显式2
      	Intent intent = new Intent();
      	intent.setClass(AActivity.this, BActivity.class);
      	startActivity(intent);
      // 显式3
      	Intent intent = new Intent();
      	intent.setClassName(AActivity.this, "com.skypan.helloworld.jump.BActivity"); // 第二个参数要给BActivity的绝对路径
      	startActivity(intent);
      // 显式4
      	Intent intent = new Intent();
      	intent.setComponent(new ComponentName(AActivity.this, "com.skypan.helloworld.jump.BActivity")); // 第二个参数要给BActivity的绝对路径
      	startActivity(intent);
      // 隐式
      	Intent intent = new Intent();
      	// 在AndroidManif.xml中设置BActivity的action后
      	// <action android:name="com.skypan.test.BActivity" />
      	// <category android:name="android.intent.category.DEFAULT" />
      	// 我们把category的类型改成default,目前还不知道为什么
      	Intent.setAction("com.skypan.test.BActivity");
      	startActivity(intent);
      
      • 数据传递方式
        • 原理:利用startActivity传入intent来传输数据
      // 假设我们目前在AActivity中,想要跳转到BActivity中去
      // 我们用显式跳转1进行跳转
      	// 数据传输步骤
      	Intent intent = new Intent(AActivity.this, BActivity.class);
      	// 1. 首先建立一个Bundle
      	Bundle bundle = new Bundle();
      	// 2. 调用方法putString,类似的还有putInt等等,接受的都是两个参数,一个是key(String类型),一个是你要传入的值(可以是各种类型)
      	bundle.putString("name", "天哥");
      	// 3. 把已经传入数据的bundle放进intent里面
      	intent.putExtras(bundle);
      	// 4. 调用startActivity的时候传入intent即可
      	startActivity(intent);
      
      // 假设我们现在已经从AActivity中跳转到BActivity中,且已经按上面的代码传入了数据
      	// 数据接收步骤
      	// 1. 从intent里面取出带有数据的bundle
      	Bundle bundle = getIntent().getExtras();
      	// 2. 从bundle中取出key对应的数据value
      	String name = bundle.getString("name");
      	// 3. 对name进行自定义的使用
      
      
      • 运用StartActivityForResult();
        • 从跳转到的activity中接收数据
      // 假设我们要从AActivity跳转到BActivity,并希望在BActivity返回后接收数据
      // 步骤1 从AActivity使用StartActivityForResult跳转到BActivity
      	// 1. 使用StartActivityForResult进行跳转
      	Intent intent = new Intent(AActivity.this, BActivity.class);
      	StartActivityForResult(intent, 0); // 第二个参数是requestCode,用于标记result存放的位置
      	
      // 步骤2 从BActivity中进行数据传输并返回到AActivity
      	// 1. 创建intent用以保存bundle
      	Intent intent = new Intent();
      	// 2. 创建bundle用以保存数据
      	Bundle bundle = new Bundle();
      	// 3. 往bundle中输入数据
      	bundle.putString("title", "我回来了");
      	// 4. 将bundle加入intent
      	intent.putExtras(bundle);
      	// 5. 将intent加入result中
      	setResult(Activity.RESULT_OK, intent); // 第一个参数可以认为是固定的
      	// 6. 调用finish从BActivity中返回到AActivity
      	finish();
      
      // 步骤3 目前BActivity已经返回到AActivity中,我们就可以在AActiviy根据之前传入的requestCode接收数据,重写调用方法OnActivityResult
      // OnActivityResult方法会在StartActivityForResult跳转到的BActivity返回后自动调用
      	@Override
      	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
              super.onActivityResult(requestCode, resultCode, data);
              Toast.makeText(AActivity.this, data.getExtras().getString("title"), Toast.LENGTH_LONG).show();
              // 一样是获得bundle后通过get方法获得数据
          }
      
    • Activity的四种启动模式

      • 设置方式,在AndroidManifest.xml中对activity的标签加入android:launchMode=""即可
      <activity android:launchMode="standard"></activity>
      
      • standard:标准模式,默认

      Activity由任务栈管理,standard模式下,每启动一个Activity,就会创建一个新实例,放入栈中,每当一个Activity被Destroy,就被拿出栈

      每跳转到一个Activity,都会调用Activity的onCreate函数

      • singleTop:Task栈顶复用模式

      当要启动的目标Activity已经位于同一个任务栈的栈顶时,不会创建新的实例,会复用栈顶的Activity,此时只会调用其onNewIntent方法;而如果要启动的目标Activity不在栈顶时,会创建新的实例,此时调用的是onCreate方法

      • singleTask:Task栈内复用模式

      当要启动的目标Activity已经在同一个任务栈中,会复用该Activity,调用其onNewIntent方法,并且在栈中,会清楚所有在Activity之上的Activity,即在Activity之后加入栈的Activity都会被弹出栈

      任务栈可以通过在AndroidManifest.xml中给activity设置android:taskAffinity来改变,如无设置会采用默认的任务栈

      • singleInstance:全局单例模式

      当要启动的目标Activity在任何一个任务栈中,就复用,此时一个Activity占有一个任务栈。

    Fragment

    • 使用的时候注意

      • 在使用时getFragmentManager()如出现错误

        改用getSupportFragmentManager()

    • Fragment有自己的生命周期

    • Fragment依赖于Activity

    • Fragment通过getActivity()可以获取所在的Activity;Activity通过FragmentManager的findFragmentById()findFragmentByTag()获取Fragment

    • Fragment和Activity是多对多的关系

    • 使用Fragment并加入到Activity中

    // 1. 建立AFragment继承Fragment
    // 2. 重写其onCreateView和onViewCreated函数
    public class AFragment extends Fragment {
        ... onCreateView(..) {}
        ... onViewCreated(...) {}
    }
    
    
    // 3. 建立对应的layout并在Fragment中在onCreateView中引入布局文件(因为他函数中接收了参数inflater),并在onViewCreated中自定义布局文件的各种属性(因为他函数中接收了view)
     	@Override
    	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            // 1. 引入布局文件
            View view = inflater.inflate(R.layout.fragment_a, container, false);
            return view;
        }
    
    	@Override
    	public void onViewCreated(View view, Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            
            // 2. 通过view和findViewById来查找布局文件中的组件并进行自定义
            ??? = view.findViewById(???);
        }
    
    // 4. 建立BFragment继承Fragment,为BFragment重复步骤123
    // 5. 建立ContainerActivity,在其布局文件activity_container.xml中声明一个FrameLayout,id为@+id/fl_container
    	<FrameLayout android:id="@+id/fl_container" .../>
    // 6. 在ContainerActivity中,向container这个FrameLayout中加入一个Fragment
    	public class ContainerActivity extends AppCompatActivity {
            
            private AFragment aFragment;
            
            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_container);
                
                // 1. 实例化AFragment
                aFragment = new AFragment();
                // 2. 把AFragment添加到Activity的container中
                getFrameManager().beginTransaction().add(R.id.fl_container, aFragment).commit();
                // note: add函数中,第一个参数接收container,第二个参数接收fragment
                // 并且要注意最后调用commit,一般在实践中把commit函数改成commiteAllowingStateLoss,意思是可以允许丢失的错误
            }
        }
    
    • 使用Fragment替换Fragment

    假设我们已经有以上的代码,此时我们在fl_container中替换一个BFragment可以这样做:

    bFragment = new BFragment();
    getFragmentManager().beginTransaction().replace(R.id.fl_container, bFragment).commitAllowingStateLoss();
    // 注意我们把add函数改成了replace函数,并把传入的aFragment改成了bFragment
    

    但是replace函数存在一定问题:replace会先remove掉原先的aFragment,再add我们替换的bFragment,所以replace会造成一定的问题,会在之后细说

    • 使用Fragment可能存在的问题
    1. 首先,Fragment和Activity虽然一起使用,但他们的生命周期是不同的,所以就会存在Fragment的任务仍在运行,而他关联的Activity却已经消失的情况,此时在Fragment中调用函数getActivity()会造成空指针错误,我们需要避免这种错误,所以我们希望你在调用方法的时候按以下方式调用:
    if (getActivity() != null) {
    	// do something
    } else {
    	// do something
    }
    

    这样就能很好地避免空指针的错误

    1. 我们之前在使用Fragment的初始化时,都是直接调用new Fragment(),没有传递参数,而我们创建一个非默认初始化函数Fragment(String s)会报错,此时我们的解决方式可以是

      // 1. 创建static的newInstance函数
      public static AFragment newInstance(String title) {
          AFragment = new AFragment();
          // 同样地,我们利用bundle来帮助我们传递参数
          Bundle bundle = new Bundle();
          bundle.putString("title", title);
          // 注意,我们这里需要调用的是fragment的setArguments函数
          fragment.setArguments(bundle);
          return fragment;
      }
      
      // 2. 在实例化时调用newInstance函数
      	// 原本是 aFragment = new AFragment();
      	// 我们改成
      	aFragment = AFragment.newInstance("我是参数");
      
      // 3. 在Fragment的其他函数中调用getArguments来获得bundle来获得传入的参数
      	if (getArguments() != null) {
              String s = getAruguments().getString("title");
              // do something
          }
      
    • Fragment回退栈应用

    我们希望加入多个Fragment的时候,像加入多个Activity一样,按返回键会返回上一个加入的Fragment中(这在原本是达不到的),我们怎么做呢?

    原本,我们按返回键就会直接返回到上一个Activity,而我们希望返回到上一个Fragment,可以这样做:

    // 将之前的更换的操作改成这样
    // 原本是这样:getFragmentManager().beginTransaction().replace(R.id.fl_container, bFragment).commitAllowingStateLoss();
    // 改成这样
    getFragmentManager().beginTransaction().replace(R.id.fl_container, bFragment).addToBackStack(null).commitAllowingStateLoss();
    
    

    即在更换之后(或之前也可以?没试过),在commit之前调用方法addToBackStack()传入参数null即可达到目的

    注意,此时我们之前提过的问题就出现了,如果我们在replace到BFragment之前,更改过AFragment的内容,在replace到BFragment然后返回到AFragment时,AFragment的内容会被重新初始化,这就是因为调用了replace函数,而replace函数是通过先remove掉AFragment,再add一个BFragment达到的替换效果,而我们希望的是AFragment的内容被保存,我们可以通过以下代码达到目的:

    // 1. 将之前的add操作更改
    // 原本是 getFrameManager().beginTransaction().add(R.id.fl_container, aFragment).commit();
    // 改成这样
    	getFrameManager().beginTransaction().add(R.id.fl_container, aFragment, "a").commit(); 
    // 在add函数中增加了一个string参数,他是识别Fragment的前提,可以加入findFragmentByTag中识别出
    
    // 2. 将之前的replace操作更改
    // 原本是 getFragmentManager().beginTransaction().replace(R.id.fl_container, bFragment).addToBackStack(null).commitAllowingStateLoss();
    // 改成这样
    	// 通过findFragmentByTag识别出aFragment
    	Fragment fragment = getFragmentManaget().findFragmentByTag("a");
    	// 将remove改成hide即可
    	if (fragment != null) {
    getFragmentManager().beginTransaction().hide(fragemt).add(R.id.fl_container, bFragment).addToBackStack(null).commitAllowingStateLoss();
     } else {
     getFragmentManager().beginTransaction().replace(R.id.fl_container, bFragment).addToBackStack(null).commitAllowingStateLoss();
     }
    

    此时我们就可以保存AFragment的状态而不是重新初始化AFragment

    • Fragment和Activity的数据传输

    为了实现Fragment和Activity之间的数据传输,我们可以这样做:

    首先,在Fragment中声明接口

    public interface IOnMessageClick {
     void onClick(String text);
    }
    

    然后,让需要通信的Activity实现这个接口

    public class ContainerActivity extends AppCompatActivity implements AFragment.IOnMessageClick {
     ...
     @Override
     public void onClick(String text) {
         // do something
     }
    }
    

    然后,在Fragment的onAttach函数中绑定与Activity的接口

    public class AFragment extends Fragment {
     ...
     private IOnMessageClick listener;
     @Override
     public void onAttach(Context context) {
         super.onAttach(context);
         try {
             listener = (IOnMessageClick) context;
         } catch (ClassCastException e) {
             throw new ClassCaseException("Activity 没有实现IOnMessageClick接口");
         }
     }
    }
    

    最后,在Fragment中调用接口函数即可传递数据

    ...
    listener.onClick("你好");
    ...
     // 此时Frament将数据为string类型的"你好"传输到Activity中供使用
    
  • 相关阅读:
    字典
    列表
    重要的方法
    一笔"狗"销,"猪"事顺利!!!
    基础数据类型
    循环,格式化,运算符
    算法——三角形图形
    算法——字母金字塔
    算法——二进制求和
    Python power函数
  • 原文地址:https://www.cnblogs.com/lwfing/p/14053976.html
Copyright © 2011-2022 走看看