安卓基础巩固(一):布局、组件、动画、Activity、Fragment

news2024/11/27 8:36:52

文章目录

  • 布局
    • LinearLayout
    • RelativeLayout
    • TableLayout
    • FrameLayout
    • ConstraintLayout
    • ListView
      • 基于ArrayAdapter
      • 自定义Adaper
      • 提升ListView的运行效率
    • RecyclerView
      • 基本属性
      • 使用案例
      • 布局(显示方式)
      • 监听事件
        • 利用View.onClickListener 和 onLongClickListener
    • ViewPager
  • 动画
    • 帧动画
    • 补间动画
    • 属性动画
  • Activity
    • AndroidMainfest.xml
    • 生命周期
      • onCreate和onStart的区别
      • onPause和onStop的区别
      • 生命周期的变化
    • Activity的启动
      • Intent
      • Bundle
    • Activity携带参数返回
    • Activity启动模式
      • 任务(task),返回栈(back stack)
      • Activity的四种启动模式
        • standard(默认模式)
        • singleTop
        • singleTask
        • singleInstance
    • Activity中获取View的宽高
      • 应用:动态调整ImageView的宽高
  • Fragment
    • Fragment的优点
    • Fragmet的生命周期
      • 生命周期变化
      • Fragment与Activity不同的生命周期
    • 加载和使用Fragmet
      • 向Activity中添加Fragment
        • 静态加载
        • 动态加载
      • 管理Fragmet
    • Fragment间通信
      • Fragment与其附着的Activity之间通信方式
    • DialogFragment

布局

安卓常用的布局方式有六种(绝对布局因灵活性太差已经弃用):

  • 线性布局
  • 相对布局
  • 网格布局
  • 表格布局
  • 帧布局
  • 约束布局
    在这里插入图片描述

LinearLayout

LinearLayout里面可以放置多个view(这里称为子view,子项)。 子view可以是TextView,Button,或者是LinearLayout,RelativeLayout等等。 它们将会按顺序依次排布为一列或一行。

常用属性

  • orientation:确定水平或竖直排布子view。 可选值有vertical和horizontal。
  • gravity:决定子view的排布方式。gravity有“重力的意思”,引申为子view会向哪个方向靠拢,gravity有几个选项可以选择,我们常用的有start,end,left,right,top,bottom。
  • 子view的layout_gravity:gravity是控制自己内部的子元素,layout_gravity是告诉父元素自己的位置。可以设置子view的layout_weight来控制空间占比,设置layout_weight的时候,一般要设置子view的layout_width(水平排布时)或者layout_height(垂直排布时)为0。
  • divider:设置divider和showDivider属性,使得子view之间有分割线。

RelativeLayout

RelativeLayout和LinearLayout类似,都是ViewGroup,能“容纳”多个子view。RelativeLayout 是一个以相对位置显示子视图的视图组。每个视图的位置可以指定为相对于同级元素的位置(例如,在另一个视图的左侧或下方)或相对于父级 RelativeLayout 区域的位置(例如在底部、左侧或中心对齐)((由 ID 确定)的位置)。

TableLayout

表格布局,通过设置表格的行列,构建布局。
常用属性
layout_column:显示在第几列
layout_columnSpan:横向跨几列
layout_columnWeight:剩余空间分配方式
layout_gravity:在网格中的显示位置
layout_row:显示在第几行
layout_rowSpan:横向跨几行
layout_rowWeight:纵向剩余空间分配

FrameLayout

帧布局:特点是子view是可以重叠的。

ConstraintLayout

ConstraintLayout 可让您使用扁平视图层次结构(无嵌套视图组)创建复杂的大型布局。它与RelativeLayout 相似,其中所有的视图均根据同级视图与父布局之间的关系进行布局,但其灵活性要高于 RelativeLayout。

注意:约束布局要求每个视图至少有两个约束条件:一个水平约束,一个垂直约束。如果缺少某个方向的约束,比如垂直,那么默认是贴近上边界。

还可以通过设定基准线,指定控件相对于基础线的约束布局。

ListView

ListView,列表视图,能够根据列表中选项个数自适应屏幕显示。ListView本身类似于布局容器,它的子View需要另外定义。在APP运行时,每个列表选项是一个子模块,这个子模块有视图,有对应的数据需要填充到视图,每个子模块还需要绑定对应的事件函数。

安卓开发中,使用了适配器设计模式来处理ListView的显示流程。

适配器模式的定义为:将一个类的接口转为客户所期待的 另一种接口,从而使得原本接口不匹配而无法工作在一起的两个类,能够在一起工作。

ListView期待的是一个有视图有数据有交互功能的列表子模块,在程序运行中,我们先把数据和视图一起加工处理为listview期待的类型,再交给listview工作。

基于ArrayAdapter

如果列表中每个选项的内容可以由一个简单的基本数据类型表示,那么可以使用ArrayAdapter来实现。
例如下边的列表,只需要显示蓝牙的地址,那么只需要一个String类型作为数据传入。
构建步骤如下:

  1. 在activity的布局文件中:声明一个ListView。
    在这里插入图片描述
  2. 额外编写一个device_name.xml,它是每个选项的视图,对于我们的需要而言,一个TextView足以。
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textSize="18sp"
    android:padding="5dp"
/>
  1. 在activity中:
    将device_name.xml构建为ArrayAdpter,然后把适配器交给ListView。当需要向列表中增加选项时,直接调用mPairedDevicesArrayAdapter.add()即可。
//初使化设备适配器存储数组
mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
mUnPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);

//设置已配队设备列表
ListView pairedListView = findViewById(R.id.pairedListView);
pairedListView.setAdapter(mPairedDevicesArrayAdapter);
pairedListView.setOnItemClickListener( mDeviceClickListener);

// 设置新查找设备列表
ListView newDevicesListView = findViewById(R.id.unPairedListView);
newDevicesListView.setAdapter(mUnPairedDevicesArrayAdapter);
newDevicesListView.setOnItemClickListener(mDeviceClickListener);

//         得到本地蓝牙句柄
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
//        //添加已配对设备到列表并显示
if (pairedDevices.size() > 0) {
    findViewById(R.id.pairedListView).setVisibility(View.VISIBLE);
    for (BluetoothDevice device : pairedDevices) {
        mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
    }
} else {
    String noDevices = "没有找到已配对的设备。" ;
    mPairedDevicesArrayAdapter.add(noDevices);
}

自定义Adaper

只能显示一段文本的listview太单调了,我们现在就来对listview的界面进行定制,让其丰富内容。
在这里插入图片描述
构建步骤如下:

  1. 在activity.xml中声明一个ListView
  2. 构建每个列表选项中的数据类Fruit:
package com.example.listview2;
public class Fruit {
private int imageID;
private String name;
private String price;
     public int getImageID() {
         return imageID;
     }
     public String getName() {
         return name;
     }
     public String getPrice() {
         return price;
     }
     public Fruit(int imageID, String name, String price) {
         this.imageID = imageID;
         this.name = name;
         this.price = price;
     }
}
  1. 构建每个列表选项布局文件fruit_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:orientation="horizontal"
     android:layout_height="wrap_content">
     <ImageView
         android:id="@+id/fruit_image"
         android:src="@drawable/apple"
         android:layout_width="100dp"
         android:layout_height="80dp"/>
     <TextView
         android:id="@+id/fruit_name"
         android:layout_gravity="center_vertical"
         android:textSize="30sp"
         android:textColor="#000000"
         android:text="name"
         android:layout_marginLeft="10dp"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"/>
     <TextView
         android:id="@+id/fruit_price"
         android:layout_gravity="center_vertical"
         android:textColor="#ff0000"
         android:text="price"
         android:textSize="30sp"
         android:layout_marginLeft="10dp"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"/>
</LinearLayout>
  1. 自定义FruitAdpter类继承自ArrayAdpter
    编写构造方法(构造方法需要传递上下文,数据内容),重写getView()函数。
package com.example.listview2;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
//用于将上下文、listview 子项布局的 id 和数据都传递过来
public class FruitAdapter extends ArrayAdapter<Fruit> {
 public FruitAdapter(@NonNull Context context, int resource, @NonNull List<Fruit> objects) {
     super(context, resource, objects);
 }
//每个子项被滚动到屏幕内的时候会被调用
     @NonNull
     @Override
     public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
     Fruit fruit=getItem(position);//得到当前项的 Fruit 实例
     //为每一个子项加载设定的布局
     View view=LayoutInflater.from(getContext()).inflate(R.layout.fruit_item,parent,false);
     //分别获取 image view 和 textview 的实例
     ImageView fruitimage =view.findViewById(R.id.fruit_image);
     TextView fruitname =view.findViewById(R.id.fruit_name);
     TextView fruitprice=view.findViewById(R.id.fruit_price);
     // 设置要显示的图片和文字
     fruitimage.setImageResource(fruit.getImageID());
     fruitname.setText(fruit.getName());
     fruitprice.setText(fruit.getPrice());
     return view;
     }
}
  1. 在activity中,准备数据,构建ListView并设置Adapter。
package com.example.listview2;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
     //第一步:定义对象
     ListView listView;
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         //第二步:绑定控件
         listView = (ListView) findViewById(R.id.list_view);
         //第三步:准备数据
         List<Fruit> fruitlist = new ArrayList<>();
         for (int i = 0; i <2 ; i++) {
             Fruit pineapple=new Fruit(R.drawable.pineapple,"菠萝","¥16.9 元/KG");
             fruitlist.add(pineapple);
             Fruit mango = new Fruit(R.drawable.mango, "芒果","¥29.9 元/kg");
             fruitlist.add(mango);
             Fruit pomegranate = new Fruit(R.drawable.pomegranate, "石榴","¥15元/kg");
             fruitlist.add(pomegranate);
             Fruit grape = new Fruit(R.drawable.grape, "葡萄","¥19.9 元/kg");
             fruitlist.add(grape);
             Fruit apple = new Fruit(R.drawable.apple, "苹果","¥20 元/kg");
             fruitlist.add(apple);
             Fruit orange = new Fruit(R.drawable.orange, "橙子","¥18.8 元/kg");
             fruitlist.add(orange);
             Fruit watermelon = new Fruit(R.drawable.watermelon, "西瓜","¥28.8元/kg");
             fruitlist.add(watermelon);
         }
         //第四步:设计每一个列表项的子布局
         //第五步:定义适配器 控件 -桥梁-数据
         FruitAdapter adapter=new FruitAdapter(MainActivity.this,R.layout.fruit_item,fruitlist);
         listView.setAdapter(adapter);
   }
}

提升ListView的运行效率

目前我们的ListView的运行效率是很低的,因为在FruitAdapter的getView()方法中,每次都将布局重新加载了一遍,当页面快速滚动的时候,这将成为性能的瓶颈。
在这里插入图片描述
优化方法一:
仅在convertView为null时才创建:
在这里插入图片描述

优化方法二:
新增内部类ViewHolder对控件实例进行缓存。

在这里插入图片描述

public class FruitAdapter extends ArrayAdapter {
    public FruitAdapter(@NonNull Context context, int resource, @NonNull List<Fruit> objects) {
        super(context, resource, objects);
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        ViewHolder viewHolder;
        if (convertView == null) {
            viewHolder = new ViewHolder();
            convertView = LayoutInflater.from(getContext()).inflate(R.layout.item, parent, false);
            viewHolder.avatar = convertView.findViewById(R.id.avatar);
            viewHolder.name = convertView.findViewById(R.id.fruit_name);
            viewHolder.price = convertView.findViewById(R.id.fruit_price);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }
        Fruit fruit = (Fruit) getItem(position);
        viewHolder.price.setText(fruit.getPrice().toString());
        viewHolder.name.setText(fruit.getName());
        viewHolder.avatar.setText(fruit.getAvatar());
        return convertView;
    }

    private final class ViewHolder {
        TextView avatar, name, price;
    }

RecyclerView

RecyclerView是ListView的升级版,它更加灵活,使用更加简单。在ListView中我们可以自己实现ViewHolder以及convertView进行优化,但是在RecyclerView中,它直接封装了ViewHolder的回收利用,也就是RecyclerView将ViewHolder标准化,我们不需要面向 view ,而是直接面向 ViewHolder 编写实现我们需要的 Adapter,这样一来,逻辑结构就变得非常清晰。

RecyclerView常常搭配线性布局和网格布局使用。

基本属性

  • itemAnimator:增删动画
  • itemDecoration:分割线

注意:RecyclerView 本身是不提供点击、长按事件的,而隔壁的 ListView 稳稳支持。对此,可能刚接触 RecyclerView 的同学会疯狂吐槽,怎么作为升级版的 RecyclerView 在这一点上还不如旧版呢?

显然不是。

ListView 中对于点击事件的处理,其实是有很大弊端的,它的 setOnItemClickListener() 方法是为子项注册点击事件,这就导致只能识别到这一整个子项,对于子项中的组件比如按钮就束手无策了。为此,RecyclerView 直接放弃了这个为子项注册点击事件的监听方法,所有点击事件都有具体 View 去注册,好处显而易见,我可以按需为组件注册点击事件,不存在点击不到的组件。

RecyclerView 的核心使用流程如下:

mRecyclerView = findView(R.id.id_recycler_view);
//设置布局管理器
mRecyclerView.setLayoutManager(mLayoutManager);
//设置adapter
mRecyclerView.setAdapter(mAdapter)
//设置Item增加、移除动画
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
//添加分割线
mRecyclerView.addItemDecoration(new DividerItemDecoration(
                getActivity(), DividerItemDecoration.HORIZONTAL_LIST));

使用案例

下面就来介绍一下 如何通过 RecyclerView 轻松实现一个普通列表:
MainActivity.java:

public class MainActivity extends AppCompatActivity {
    private RecyclerView mRecyclerView;
    private MyAdapter mMyAdapter;
    private LinearLayoutManager mLayoutManager;
    private List<String> list;

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initData();

        mRecyclerView = findViewById(R.id.recycler_view);
        mMyAdapter = new MyAdapter(list);
        mLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(mLayoutManager);
        mRecyclerView.setAdapter(mMyAdapter);
    }

    private void initData() {
        list = new ArrayList<>();
        for (int i = 0; i <= 20; i++) {
            list.add("Item " + i);
        }
    }
}

MyAdapter.java:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

    //数据源
    private List<String> mList;

    public MyAdapter(List<String> list) {
        mList = list;
    }

    //返回item个数
    @Override
    public int getItemCount() {
        return mList.size() ;
    }

    //创建ViewHolder
    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    return new NormalHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false));
    }

    //填充视图
    @Override
    public void onBindViewHolder(@NonNull final MyAdapter.ViewHolder holder, final int position) {
        holder.mView.setText(mList.get(position));
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        public TextView mView;

        public ViewHolder(View itemView) {
            super(itemView);
            mView = itemView.findViewById(R.id.text_view);
        }
    }
}

布局(显示方式)

listView默认是垂直布局,而recyclerView则更加灵活,运行我们自己设置它的布局。

可通过LayoutManager(LinearLayoutManager,GridLayoutManager,StaggeredGridLayoutManager )设置线性布局、网格布局、瀑布流布局;

监听事件

RecycylerView 并没有处理点击事件的监听器,所以如果要监听 RecycylerView 的点击事件,我们需要自己写监听器。
下面就简单介绍几种实现方法。
推荐使用方法一和方法三

  • 方法一:利用View.onClickListener 和 onLongClickListener
  • 方法二:利用RecyclerView.OnItemTouchListener
  • 方法三:利用GestureDetector(手势检测类)对方法二优化

利用View.onClickListener 和 onLongClickListener

  1. 在adapter中新建两个内部接口:
public interface OnItemClickListener {
    void onItemClick(View view, int position);
}
 
public interface OnItemLongClickListener {
    void onItemLongClick(View view, int position);
}
  1. 新建两个私有变量用于保存用户设置的监听器,并公开一个设置监听器的方法:
private OnItemClickListener mOnItemClickListener;
private OnItemLongClickListener mOnItemLongClickListener;
 
public void setOnItemClickListener(OnItemClickListener mOnItemClickListener) {
    this.mOnItemClickListener = mOnItemClickListener;
}
 
public void setOnItemLongClickListener(OnItemLongClickListener mOnItemLongClickListener) {
    this.mOnItemLongClickListener = mOnItemLongClickListener;
}
  1. 在onBindViewHolder方法内,实现回调:
    @Override
    public void onBindViewHolder(final MyViewHolder holder, int position) {
        holder.tvTest.setText(stringList.get(position));
//        ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
//        lp.height = (int) (100 + Math.random() * 300);
//        holder.itemView.setLayoutParams(lp);
        //判断是否设置了监听器
        if(mOnItemClickListener != null){
            //为ItemView设置监听器
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int position = holder.getLayoutPosition(); // 1
                    mOnItemClickListener.onItemClick(holder.itemView,position); // 2
                }
            });
        }
        if(mOnItemLongClickListener != null){
            holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    int position = holder.getLayoutPosition();
                    mOnItemLongClickListener.onItemLongClick(holder.itemView,position);
                    //返回true 表示消耗了事件 事件不会继续传递
                    return true;
                }
            });
        }
    }

这里实际上用到了子 Item View 的 onClickListener 和onLongClickListener这两个监听器,如果当前子item view被点击了,会触发点击事件进行回调,然后在内部接口处获取当前点击位置的position值,接着在我们保存的用户设置的监听器处进行再次回调,而这一次的回调是我们自己手动添加的,需要实现上面所述的接口。
修改完 TestAdapter后,我们接着在 MainActivity 中设置监听器,采用匿名内部类的形式实现了 onItemClickListener 、 onItemLongClickListener 接口,这种写法与一般的设置监听器的流程相同:

TestAdapter mTestAdapter = new TestAdapter(getList());
mTestAdapter.setOnItemClickListener(new TestAdapter.OnItemClickListener() {
    @Override
    public void onItemClick(View view, int position) {
        Toast.makeText(MainActivity.this, "click " + getList().get(position), Toast.LENGTH_SHORT).show();
    }
});
mTestAdapter.setOnItemLongClickListener(new TestAdapter.OnItemLongClickListener() {
    @Override
    public void onItemLongClick(View view, int position) {
        Toast.makeText(MainActivity.this,"long click "+getList().get(position),Toast.LENGTH_SHORT).show();
    }
});
rvTest.setAdapter(mTestAdapter);

ViewPager

一个简单的页面切换组件。
使用案例:

编写三个页面进行切换。

  1. 首先创建3个xml布局文件
  2. 在activity的布局文件中声明ViewPager
  3. 创建Adapter继承PagerAdapter,重写方法:
    • getCount():获取viewpager中有多少个view
    • instantiateItem():
      • 将给定位置的view添加到viewgroup中,创建并显示处理
      • 返回一个代表新增页面的Object(key),通常都是直接返回view本身就可以了,当然你也可以自定义自己的key,但是key和每个view要一一对应的关系。
  4. isViewFromObject()
    判断instantiateItem(Viewgroup,int)函数所返回的key与一个页面视图是否是代表的同一个视图(即他俩是否是对应的,对应的表示同一个view),通常我们直接写成return view==object
  5. destroyItem()
    移除一个给定位置的页面,适配器有责任从容器中删除这个视图,这是为了确保在finish update(viewgroup)返回时视图能够被移除。

adapter的代码:

package com.example.myviewpager;

import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.viewpager.widget.PagerAdapter;

import java.util.List;

public class MyAdapter extends PagerAdapter {

    private List<View> listview;

    public MyAdapter(List<View> listview) {
        this.listview = listview;
    }

    @NonNull
    @Override
    public Object instantiateItem(@NonNull ViewGroup container, int position) {
        container.addView(listview.get(position),0);
        return listview.get(position);
    }

    @Override
    public int getCount() {
        return listview.size();
    }

    @Override
    public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
        return view==object;
    }

    @Override
    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        container.removeView(listview.get(position));
    }
}

activity:


import android.view.LayoutInflater;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import androidx.viewpager.widget.ViewPager;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        LayoutInflater lf=getLayoutInflater().from(this);
        View view1=lf.inflate(R.layout.layout1,null);
        View view2=lf.inflate(R.layout.layout2,null);
        View view3=lf.inflate(R.layout.layout3,null);
        List<View> viewList=new ArrayList<>();
        viewList.add(view1);
        viewList.add(view2);
        viewList.add(view3);
        ViewPager viewPager=findViewById(R.id.vp);//获取viewpager
        MyAdapter myAdapter=new MyAdapter(viewList);
        viewPager.setAdapter(myAdapter);
    }
}

动画

帧动画

Frame Animation
用多张图片来组成动画。一帧帧的播放图片,利用人眼视觉残留原理,给我们带来动画的感觉。它的原理的GIF图片、电影播放原理一样。
我们可以使用AnimationDrawable 来实现动画效果。

补间动画

Tween Animation
补间动画就是我们只需指定开始、结束的“关键帧“,而变化中的其他帧由系统来计算,不必自己一帧帧的去定义。
Android使用Animation代表抽象动画,包括四种子类:AlphaAnimation(透明度动画)、ScaleAnimation(缩放动画)、TranslateAnimation(位移动画)、RotateAnimation(旋转动画)

一般都会采用动画资源文件来定义动画,把界面与逻辑分离
定义好anim文件后,我们可以通过AnimationUtils工具类来加载它们,加载成功后返回一个Animation。然后就可以通过View的startAnimation(anim)开始执行动画了。

属性动画

直接更改我们对象的属性。在上面提到的Tween Animation中,只是更改View的绘画效果而View的真实属性是不改变的
常用 Animator 类,ValueAnimator 等
Animator可加载动画资源文件
ValueAnimator可使用内置估值器,添加监听AnimatorUpdateListener,在每次变化时修改view的属性

Activity

Activity 是一个应用组件,用户可与其提供的屏幕进行交互,以执行拨打电话、拍摄照片、发送电
子邮件或查看地图等操作。 每个 Activity 都会获得一个用于绘制其用户界面的窗口。窗口通常会
充满屏幕,但也可小于屏幕并浮动在其他窗口之上。

AndroidMainfest.xml

也可以简称为「manifest文件」。清单文件非常重要,它告诉系统我们的app有哪些activity,用到了什么权限等等信息。
如果要新建activity,需要在清单中注册。

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

从这个默认的清单文件中我们可以得知,activity是属于application的。application就是我们的应用。
application标签中也指定了各种元素,例如应用的图标,名字,主题等等。

MainActivity是应用启动的第一个activity。可以观察到它设置了action和category属性。

  • android.intent.action.MAIN 决定应用程序最先启动的Activity。
  • android.intent.category.LAUNCHER 表示可以在手机“桌面”上看到应用图标

生命周期

在这里插入图片描述

onCreate和onStart的区别

  • onCreate 在系统首次创建 Activity 时触发。Activity会在创建后进入已创建状态。onCreate方法处应该是我们创建视图,准备数据的位置。
  • 当 Activity 进入“已开始”状态时,系统会调用此回调。onStart() 调用使 Activity 对用户可见,因为应用会为 Activity 进入前台并支持交互做准备。
  • onCreate方法在activity的生命周期中只会执行一次,而onStart方法在activity停止时,执行完onRestart后,会再次执行。

onPause和onStop的区别

  • onPause() 执行非常简单,而且不一定要有足够的时间来执行保存操作。== 因此,您不应使用onPause() 来保存应用或用户数据、进行网络调用,或执行数据库事务。因为在该方法完成之前,此类工作可能无法完成。==
  • 在 onStop() 方法中,应用应释放或调整应用对用户不可见时的无用资源。例如,应用可以暂停动画效果,或从细粒度位置更新切换到粗粒度位置更新。 使用 onStop() 而非 onPause() 可确保与界面相关的工作继续进行,即使用户在多窗口模式下查看您的 Activity 也能如此。 在 onStop() 关闭CPU 相对密集的操作。

生命周期的变化

  1. 启动后退出:

onCreate
onStart
onResume
onWindowFocusChanged: hasFocus: true
onWindowFocusChanged: hasFocus: false
onPause
onStop
onDestroy

2.启动后按home键

Act1: onCreate
Act1: onStart
Act1: onResume
Act1: onWindowFocusChanged: hasFocus: true
// 按home键
Act1: onWindowFocusChanged: hasFocus: false
Act1: onPause
Act1: onStop
// 再回来
Act1: onRestart
Act1: onStart
Act1: onResume
Act1: onWindowFocusChanged: hasFocus: true
// 按返回键退出act
Act1: onWindowFocusChanged: hasFocus: false
Act1: onPause
Act1: onStop
Act1: onDestroy

  1. 旋转手机(横竖屏切换时的生命周期变化)
    [Life]: onCreate
    [Life]: onStart
    [Life]: onResume
    [Life]: onWindowFocusChanged: hasFocus: true
    // 横屏
    [Life]: onPause
    [Life]: onStop
    [Life]: onDestroy
    [Life]: onCreate
    [Life]: onStart
    [Life]: onResume
    [Life]: onWindowFocusChanged: hasFocus: true
    // 竖屏
    [Life]: onPause
    [Life]: onStop
    [Life]: onDestroy
    [Life]: onCreate
    [Life]: onStart
    [Life]: onResume
    [Life]: onWindowFocusChanged: hasFocus: true
    // 返回
    [Life]: onWindowFocusChanged: hasFocus: false
    [Life]: onPause
    [Life]: onStop
    [Life]: onDestroy
  2. 两个activity切换

Act1: onCreate
Act1: onStart
Act1: onResume
Act1: onWindowFocusChanged: hasFocus: true
Act1: onPause // 切换到Act2
Act1: onWindowFocusChanged: hasFocus: false
Act2: onCreate
Act2: onStart
Act2: onResume
Act2: onWindowFocusChanged: hasFocus: true
Act1: onStop
Act2: onWindowFocusChanged: hasFocus: false // 再切换回Act1
Act2: onPause
Act1: onRestart
Act1: onStart
Act1: onResume
Act1: onWindowFocusChanged: hasFocus: true
Act2: onStop
Act2: onDestroy
Act1: onWindowFocusChanged: hasFocus: false
Act1: onPause
Act1: onStop
Act1: onDestroy

在这里插入图片描述

  1. 弹出一个AlertDialog
    会调用onWindowFocusChanged
    onWindowFocusChanged: hasFocus: false
    onWindowFocusChanged: hasFocus: true

Activity的启动

Intent

Intent,直译为“意图”。我们把信息包裹在intent对象中,然后执行。

这里用到一个很常见的方法startActivity (Intent intent) 。 startActivity 属于Context类,Activity是Context的子类。

从LoginActivity 跳转到 WaitActivity:

Intent intent = new Intent(LoginActivity.this, WaitActivity.class);
startActivity(intent);

在跳转去下一个页面时,我们可能会想携带一些信息到下一个界面去。例如携带一些文本,数字等等,或者是一个对象。 这些信息我们可以交给Intent,传递到下一个activity去。下一个activity中拿到我们传入的Intent。

Intent intent = new Intent(getApplicationContext(), SendParamsDemo.class);
intent.putExtra(SendParamsDemo.K_INT, 100);
intent.putExtra(SendParamsDemo.K_BOOL, true);
intent.putExtra(SendParamsDemo.K_STR, "Input string");
startActivity(intent);

在另外一个activity中直接通过get_获取相应的参数。

int i = intent.getIntExtra(K_INT, -1);
boolean b = intent.getBooleanExtra(K_BOOL, false);
String str = intent.getStringExtra(K_STR);

Bundle

实际上在安卓开发我们使用Bundle用于传递数据;它保存的数据,是以key-value(键值对)的形式存在的。

经常使用Bundle在Activity之间传递数据,传递的数据可以是boolean、byte、int、long、float、double、string等基本类型或它们对应的数组,也可以是对象或对象数组。当Bundle传递的是对象或对象数组时,必须实现Serializable 或Parcelable接口。

intent其实是调用了bundle相应的put函数,也就是说,intent内部还是用bundle来实现数据传递的,只是封装了一层而已。

Activity携带参数返回

在一个主界面(主Activity)通过意图跳转至多个不同子Activity上去,当子模块的代码执行完毕后再次返回主页面,将子Activity中得到的数据显示在主界面/完成的数据交给主Activity处理。这种带数据的意图跳转需要使用下边三个方法:

  • startActivityForResult(Intent intent, int requestCode);
  • setResult(int resultCode, Intent data)
  • onActivityResult(int requestCode, int resultCode, Intent data)

下边的例子中有2个activity作为示范:ForResultFirstAct 和ForResultSecondAct 。

步骤:

  1. 主activity:调用startActivityForResult(Intent intent, int requestCode);

第一个参数:一个Intent对象,用于携带将跳转至下一个界面中使用的数据,使用putExtra(A,B)方法,此处存储的数据类型特别多,基本类型全部支持。
第二个参数:如果> = 0,当Activity结束时requestCode将归还在onActivityResult()中。以便确定返回的数据是从哪个Activity中返回,用来标识目标activity。

private static final int REQ_CODE = 10;
startActivityForResult(new Intent(getApplicationContext(),
ForResultSecondAct.class), REQ_CODE);
  1. ForResultSecondAct 是第二个activity。它可以设置返回时携带的数据,然后调用setResult(RESULT_OK, resultIntent);
Intent resultIntent = new Intent();
resultIntent.putExtra(K_TITLE, mTitleEt.getText().toString());
resultIntent.putExtra(K_SUB_TITLE, mSubTitleEt.getText().toString());
setResult(RESULT_OK, resultIntent);
finish();

其中RESULT_OK 是Activity类的静态常量。可用于代表操作的结果,除此外还有其他状态:

/** Standard activity result: operation canceled. */
public static final int RESULT_CANCELED = 0;
/** Standard activity result: operation succeeded. */
public static final int RESULT_OK = -1;
/** Start of user-defined activity results. */
public static final int RESULT_FIRST_USER = 1;
  1. 在主activity中重写onActivityResult(int requestCode, int resultCode, Intent data),验证requestCode、resultCode后,获取到返回的数据:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
	super.onActivityResult(requestCode, resultCode, data);
	switch (requestCode) {
		case REQ_CODE:
		if (resultCode == RESULT_OK) {
		if (data != null) {
				mTitleTv.setText(data.getStringExtra(ForResultSecondAct.K_TITLE));
				mSubTitleTv.setText(data.getStringExtra(ForResultSecondAct.K_SUB_TITLE));
			}
		} else {
			Toast.makeText(getApplicationContext(), "未保存修改",
			Toast.LENGTH_SHORT).show();
		}
		break;
	}
}

Activity启动模式

任务(task),返回栈(back stack)

任务是指在执行特定作业时与用户交互的一系列 Activity。 这些 Activity 按照各自的打开顺序排列在堆栈(即返回栈)中。

Activity的四种启动模式

在这里插入图片描述

standard(默认模式)

默认模式。如果不指定启动模式,则会使用这个模式。 系统在启动 Activity 的任务中创建 Activity 的新实例并向其传送 Intent。Activity 可以多次实例化,而每个实例均可属于不同的任务,并且一个任务可以拥有多个实例。

singleTop

可以有多个实例,但是不允许多个相同Activity叠加。即,如果Activity在栈顶的时候,启动相同的Activity,不会创建新的实例,而会调用其onNewIntent方法

目标作用是在栈顶添加activity实例或走栈顶activity的onNewIntent() 方法。若目标activity已在栈顶,则不会新建实例。

如果当前任务的顶部已存在 Activity 的一个实例,则系统会通过调用该实例的 onNewIntent() 方法向其传送 Intent,而不是创建 Activity 的新实例。Activity 可以多次实例化,而每个实例均可属于不同的任务,并且一个任务可以拥有多个实例(但前提是位于返回栈顶部的 Activity 并不是 Activity 的现有实例)。

例如,假设任务的返回栈包含根 Activity A 以及 Activity B、C 和位于顶部的 D(堆栈是 A-B-C-D;D 位于顶部)。收到针对 D 类 Activity 的 Intent。如果 D 具有默认的 “standard” 启动模式,则会启动该类的新实例,且堆栈会变成 A-B-C-D-D。但是,如果 D 的启动模式是 “singleTop”,则 D 的现有实例会通过 onNewIntent() 接收 Intent,因为它位于堆栈的顶部;而堆栈仍为 A-B-C-D。但是,如果收到针对 B 类 Activity 的 Intent,则会向堆栈添加 B 的新实例,即便其启动模式为 “singleTop” 也是如此。

singleTask

只有一个实例。在同一个应用程序中启动他的时候,若Activity不存在,则会在当前task创建一个新的实例,若存在,则会把task中在其之上的其它Activity destory掉并调用它的onNewIntent方法。

  • 若目标activity已在栈中,则会销毁在目标activity之上的其他实例,此时目标activity来到栈顶。
  • 若目标activity是 “MAIN” activity,能被Launcher启动。那么按home键将App退到后台,在桌面上点击App图标。目标activity之上的页面都会被销毁掉,并调用目标activity的onNewIntent()方法。
  • 系统创建新任务并实例化位于新任务底部的 Activity。但是,如果该 Activity 的一个实例已存在于一个单独的任务中,则系统会通过调用现有实例的 onNewIntent() 方法向其传送 Intent,而不是创建新实例。一次只能存在 Activity 的一个实例。

注:尽管 Activity 在新任务中启动,但是用户按“返回”按钮仍会返回到前一个 Activity。

singleInstance

只有一个实例,并且这个实例独立运行在一个task中,这个task只有这个实例,不允许有别的Activity存在。

与 “singleTask” 相同,只是系统不会将任何其他 Activity 启动到包含实例的任务中。该 Activity 始终是其任务唯一仅有的成员;由此 Activity 启动的任何 Activity 均在单独的任务中打开。

例如,A,B,C 3个Activity,只有B是以singleInstance模式启动,其他是默认模式。 页面启动顺序是 A -> B -> C,B会自己在一个task中;栈情况如下(方括号表示在前台):

stack    |   | => |   | |   | => |   | |   | 
         |   |    |   | |   |    | C | |   | 
         | A |    | A | | B |    | A | | B | 
task id: [1082]   1082  [1083]   [1082] 1083

此时屏幕上显示是C的界面,按返回键,C被销毁,显示的是A的界面;再返回,A被销毁,原A和C所在的task结束。此时显示B的界面。

如果在只剩下B的时候,去启动C;由于B是singleInstance模式,B所在的栈只能有一个activity,则会新建一个task来存放C

stack     |   | => |   | |   | 
          |   |    |   | |   | 
          | B |    | B | | C | 
task id:  [1083]   1083  [1084]  

这4种启动模式各有特点。在开发中我们根据实际情况选择不同的启动模式。 例如
“根页面”可以考虑用singleTask的模式。

Activity中获取View的宽高

有些时候我们需要获取到View的宽高信息。

在onCreate和onResume中尝试view.getWidth()或是view.getHeiht()时,我们会发现获取到的是0。

Activity视图在创建完成后,各个子view并不一定被加载完成。 获取宽高正确的方法有哪些呢?

  • 方法1 - 在Activity的onWindowFocusChanged获取宽高
    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        // 在这里我们可以获取到View的真实宽高
        Log.d(TAG, "onWindowFocusChanged: mBtn1.getWidth == " + mBtn1.getWidth());
    }

  • 方法2-使用ViewTreeObserver的OnGlobalLayoutListener回调
    获取View的ViewTreeObserver,添加回调
    ViewTreeObserver vto = mBtn1.getViewTreeObserver();
    vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            int height = mBtn1.getHeight();
            int width = mBtn1.getWidth();
            Log.d(TAG, "onGlobalLayout: mBtn1 " + width + ", " + height);
            mBtn1.getViewTreeObserver().removeOnGlobalLayoutListener(this);
        }
    });

  • 方法3-使用View.post(Runnable action)方法
    例如我们在onCreate中post一个Runnable
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mBtn1 = findViewById(R.id.btn1);
    Log.d(TAG, "mBtn1 post runnable");
    mBtn1.post(new Runnable() {
        @Override
        public void run() {
            Log.d(TAG, "mBtn1: " + mBtn1.getWidth() + ", " + mBtn1.getHeight());
        }
    });
}

可以获取到view的宽高。从log的时间上可以看出,在view加载完毕后,执行的Runnable。

06-19 11:54:17.865 28009-28009/com.rustfisher.basic4 D/rustApp: mBtn1 post runnable
06-19 11:54:17.867 28009-28009/com.rustfisher.basic4 D/rustApp: [act2] onResume
06-19 11:54:17.899 28009-28009/com.rustfisher.basic4 D/rustApp: mBtn1: 355, 144

应用:动态调整ImageView的宽高

获取到view的宽高后,我们可以动态地调整ImageView的高度。 假设图片宽高为704 * 440。xml中设置scaleType为fitXY。已知ImageView的宽度是固定的,我们可以调整高度。

<ImageView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:scaleType="fitXY"/>

根据图片真实大小来重设ImageView的高度。

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    resetIntroIvParams();
}

private void resetIntroIvParams() {
    int height = mIntroIv.getHeight(); // 704 * 440
    int wid = mIntroIv.getWidth();
    if (height > 0 && wid > 0) {
        ViewGroup.LayoutParams layoutParams = mIntroIv.getLayoutParams();
        layoutParams.height = (int) (wid * 440.0 / 704.0);
        mIntroIv.setLayoutParams(layoutParams);
    }
}

Fragment

Fragment,直译为“碎片”,“片段”。 Fragment 表示 FragmentActivity 中的行为或界面的一部分。您可以在一个 Activity 中组合多个片段,从而构建多窗格界面,并在多个 Activity 中重复使用某个片段。

注意:

  • Fragment必须始终托管在 Activity 中,其生命周期直接受宿主 Activity 生命周期的影响。例如,当 Activity 暂停时,Activity 的所有片段也会暂停;当 Activity 被销毁时,所有Fragment也会被销毁。不过,当 Activity 正在运行(处于已恢复生命周期状态)时,您可以独立操纵每个Fragment,如添加或移除Fragment。当执行此类Fragment事务时,您也可将其添加到由 Activity 管理的返回栈 — Activity 中的每个返回栈条目都是一条已发生片段事务的记录。借助返回栈,用户可以通过按返回按钮撤消片段事务(后退)。
  • 当您将Fragment作为 Activity 布局的一部分添加时,其位于 Activity 视图层次结构的某个 ViewGroup 中,并且Fragment会定义其自己的视图布局。您可以通过在 Activity 的布局文件中声明Fragment,将其作为 元素插入您的 Activity 布局,或者通过将其添加到某个现有的 ViewGroup,利用应用代码将其插入布局。

Fragment的优点

  • Fragment加载灵活,替换方便。定制你的UI,在不同尺寸的屏幕上创建合适的UI,提高用户体验。
  • 可复用,页面布局可以使用多个Fragment,不同的控件和内容可以分布在不同的Fragment上。
  • 使用Fragment,可以少用一些Activity。一个Activity可以管辖多个Fragment。

Fragmet的生命周期

Fragment 类的代码与 Activity 非常相似。它包含与 Activity 类似的回调方法,如 onCreate()、onStart()、onPause() 和 onStop()。实际上,如果您要将现有 Android 应用转换为使用片段,可能只需将代码从 Activity 的回调方法移入片段相应的回调方法中。

在这里插入图片描述

生命周期变化

  1. Fragmet被创建时:

onAttach()
onCreate()
onCreateView()
onActivityCreated()

  1. Fragment对用户可见时:

onStart()
onResume()

  1. Fragmet进入“后台模式”时:

onPause()
onStop()

  1. Fragmet被销毁或者持有它的activity被销毁时:

onPause()
onStop()
onDestroyView()
onDestroy()
onDetach()

Fragment与Activity不同的生命周期

Fragment的大部分状态都和Activity很相似,但fragment有一些新的状态。

Fragment不同于Activity的生命周期:

  • onAttached() —— 当fragment被加入到activity时调用(在这个方法中可以获得所在的activity)。
  • onCreateView() —— 当activity要得到fragment的layout时,调用此方法,fragment在其中创建自己的layout(界面)。
  • onActivityCreated() —— 当activity的onCreated()方法返回后调用此方法
  • onDestroyView() —— 当fragment中的视图被移除的时候,调用这个方法。
  • onDetach() —— 当fragment和activity分离的时候,调用这个方法。

一旦activity进入resumed状态(也就是running状态),你就可以自由地添加和删除fragment了。因此,只有当activity在resumed状态时,fragment的生命周期才能独立的运转,其它时候是依赖于activity的生命周期变化的。

对于 Activity 生命周期与片段生命周期而言,二者最显著的差异是在其各自返回栈中的存储方式。

  • 默认情况下,Activity 停止时会被放入由系统管理的 Activity 返回栈中
  • 而Fragment需要我们在移除Fragment的事务执行期间通过调用 addToBackStack() 显式请求保存实例时,系统才会将片段放入由宿主 Activity 管理的返回栈。

加载和使用Fragmet

向Activity中添加Fragment

静态加载

在 Activity 的布局文件内声明片段。 在本例中,您可以将片段当作视图来为其指定布局属性。例如,以下是拥有两个片段的 Activity 的布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <fragment android:name="com.example.news.ArticleListFragment"
            android:id="@+id/list"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="match_parent" />
    <fragment android:name="com.example.news.ArticleReaderFragment"
            android:id="@+id/viewer"
            android:layout_weight="2"
            android:layout_width="0dp"
            android:layout_height="match_parent" />
</LinearLayout>

<fragment> 中的android:name属性指定要在布局中进行实例化的 Fragment 类。

创建此 Activity 布局时,系统会将布局中指定的每个片段实例化,并为每个片段调用 onCreateView() 方法,以检索每个片段的布局。系统会直接插入片段返回的 View,从而代替 <fragment> 元素。

注意:每个片段都需要唯一标识符,重启 Activity 时,系统可使用该标识符来恢复片段(您也可以使用该标识符来捕获片段,从而执行某些事务,如将其移除)。

不给fragment指定id会报错!!!

动态加载

通过编程方式将片段添加到某个现有 ViewGroup。 在 Activity 运行期间,您可以随时将片段添加到 Activity 布局中。您只需指定要将片段放入哪个 ViewGroup。
步骤:

  • ①准备好Fragment xml布局文件

  • ②新建一个类,继承自Fragment;在这个类中找到Fragment布局文件

  • ③在Activity中使用FragmentManager来操作Fragment

  • ④别忘了commit

准备fragment.xml

新建一个类FirstFragment.java,继承自Fragment。复写onCreateView方法。在onCreateView方法中,可以操作Fragment上的控件。

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_first, container,false);
//    fragment_first是自定义好的布局
//    如果此Fragment上放了控件,比如Button,Edittext等。可以在这里定义动作
  btn_fragment1_send = (Button) rootView.findViewById(R.id.btn_fragment1_1);
//...
        return rootView;
    }

准备一个位置给Fragment,比如在activity_main.xml中用Framelayout来占位。

在MainActivity.java里,先获得FragmentManager,得到FragmentTransaction。Fragment的添加删除等操作由FragmentTransaction来完成。

f1 = new FirstFragment();    //    获取实例
f2 = new SecondFragment();    //
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.layout_container1,f1);    //    添加
fragmentTransaction.replace(R.id.layout_container1,f1);    //    替换
// 或者也可以写成
fragmentTransaction.replace(R.id.layout_container1,new FirstFragment());
//              fragmentTransaction.addToBackStack(null);   //添加到返回栈,这样按返回键的时候能返回已添加的fragment
fragmentTransaction.commit();    //别忘了commit
//    移除操作 getFragmentManager().beginTransaction().remove(f1).commit();

管理Fragmet

如要管理 Activity 中的片段,您需使用 FragmentManager。如要获取它,请从您的 Activity 调用 getSupportFragmentManager()。

可使用 FragmentManager 执行的操作包括:

  • 通过 findFragmentById()(针对在 Activity 布局中提供界面的片段)或 findFragmentByTag()(针对提供或不提供界面的片段)获取 Activity 中存在的片段。
  • 通过 popBackStack()(模拟用户发出的返回命令)使片段从返回栈中弹出。
  • 通过 addOnBackStackChangedListener() 注册侦听返回栈变化的侦听器。
  • 打开一个 FragmentTransaction,通过它来执行某些事务,如添加和移除片段。

如要在您的 Activity 中执行片段事务(如添加、移除或替换片段),则必须使用 FragmentTransaction 中的 API。如下所示,您可以从 FragmentActivity 获取一个 FragmentTransaction 实例:

FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

然后,您可以使用 add() 方法添加一个片段,指定要添加的片段以及将其插入哪个视图。例如:

ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();

传递到 add() 的第一个参数是 ViewGroup,即应放置片段的位置,由资源 ID 指定,第二个参数是要添加的片段。 一旦您通过 FragmentTransaction 做出了更改,就必须调用 commit() 以使更改生效。

Fragment间通信

在Fragment的java文件中,可以使用getActivity()来获得调用它的activity,然后再找到另一个Fragment,进行通信。

getActivity().getFragmentManager().findFragmentById(R.id.fragment_list);
但这样做耦合度太高,不方便后续的修改操作。

Fragment与其附着的Activity之间的通信,都应该由Activity来完成;不能是多个Fragment之间直接通信。

Fragment与其附着的Activity之间通信方式

1.在发起事件的Fragment中定义一个接口,接口中声明你的方法。

2.在onAttach方法中要求Activity实现该接口。

3.在Activity中实现该方法。

例如一个activity中布置了2个Fragment,它们之间的通信要依靠activity来完成

代码:ListStoreActivity.java NewItemFragment.java ListStoreFragment.java
布局文件为:liststore.xml new_item_fragment.xml

ListStoreFragment.java 使用前面定义的界面

public class ListStoreFragment extends ListFragment{
/// 继承自ListFragment,已经封装好了listview
/// 不需要自己写ListView了
}

NewItemFragment.java

    /**
     * 声明一个接口,定义向activity传递的方法
     * 绑定的activity必须实现这个方法
     */
    public interface OnNewItemAddedListener {
        public void newItemAdded(String content);
    }
    private OnNewItemAddedListener onNewItemAddedListener;
    private Button btnAddItem;
    /*复写onAttach方法*/
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            onNewItemAddedListener = (OnNewItemAddedListener) activity;
        } catch (ClassCastException e){
            throw new ClassCastException(activity.toString() + "must implement OnNewItemAddedListener");
        }
    }

ListStoreActivity.java 加载主视图liststore.xml;
两个Fragment通过ListStoreActivity来通信

在onCreate方法中获取ListStoreFragment的实例;并且复写newItemAdded方法,在里面加上业务逻辑

public class ListStoreActivity extends Activity implements OnNewItemAddedListener{

    private ArrayList<String> data;
    private ArrayAdapter<String> adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.liststore);
        data = new ArrayList<String>();
        // 把data装入adapter中
        adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, data);
        // ListFragment并不需要再定义一个listview        
        ListStoreFragment listStoreFragment = (ListStoreFragment) getFragmentManager().findFragmentById(R.id.fragment_listview);
        listStoreFragment.setListAdapter(adapter);
    }

    @Override
    public void newItemAdded(String content) {
        //  复写接口中的方法,业务代码在这里实现
        if(!content.equals("")) {
            data.add(content);
            adapter.notifyDataSetChanged();
        }
    }
}

Fragment跟Activity的其他通信方式:

  • 通过构造器传递信息

在Activity中构造Fragment的时候,可以将需要传递的数据封装在成一个Bundle对象,然后通过setArguments()方法传递参数,例子如下
Activity中设置Bundle:

FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
Fragment fragTop = new FrameTop();
fragmentTransaction.replace(R.id.frame1,fragTop);
Bundle bundle = new Bundle();
fragTop.setArguments(bundle);
bundle.putString("name","fragTop");
fragmentTransaction.commit();

Fragment中获取数据:

 @Override
 public void onAttach(@NonNull Context context) {
     super.onAttach(context);
     Bundle arguments = getArguments();
     String name = arguments.getString("name");
 }

  • 通过广播

  • 通过EventBus
    通过EventBus不仅可以实现Activity与Fragment, 还可以实现Fragment与Fragment之间通信,只要是注册了EventBus的并且可见地方都可以收到它发出的消息。

EventBus是一个开源库,它使用的是发布/订阅模式来实现组件之间的通信,相对于广播机制,handler机制等,其所需要的代码更少,耦合度更低,下面是一张官方的图说明其工作方式。

  • 通过Activity和Fragment共用ViewModel

DialogFragment

弹窗,是常见的一种提示方式。市面上有多种多样、五彩缤纷的弹窗。
在这里插入图片描述
构建步骤:

  1. 准备xml布局:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:padding="12dp">

    <TextView
        android:id="@+id/title_tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textColor="#111111"
        android:textSize="16sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/content_tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:gravity="center"
        android:textColor="#111111"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/title_tv" />

</androidx.constraintlayout.widget.ConstraintLayout>

  1. 新建弹窗类,继承自DialogFragmet

新建一个SimpleDialog类继承DialogFragment。

  • 在onCreate方法中接收传入的数据。传递数据使用了Bundle。
  • 在onCreateView方法中,使用上文建立的layout。
  • 在onViewCreated方法中进行ui操作。
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;

public class SimpleDialog extends DialogFragment {
    public static final String K_TITLE = "k_title"; // 传输数据时用到的key
    public static final String K_CONTENT = "k_content";

    private String title;
    private String content;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Bundle in = getArguments();
        if (in != null) {
            title = in.getString(K_TITLE);
            content = in.getString(K_CONTENT);
        }
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.dialog_simple, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        TextView titleTv = view.findViewById(R.id.title_tv);
        TextView contentTv = view.findViewById(R.id.content_tv);

        titleTv.setText(title);
        contentTv.setText(content);
    }
}

把这个窗口弹出来。我们使用DialogFragment.show(@NonNull FragmentManager manager, @Nullable String tag)方法。

    private void popSimpleDialog1(String title, String content) {
        SimpleDialog dialog = new SimpleDialog();
        Bundle bundle = new Bundle();
        bundle.putString(SimpleDialog.K_TITLE, title);
        bundle.putString(SimpleDialog.K_CONTENT, content);
        dialog.setArguments(bundle);
        dialog.show(getSupportFragmentManager(), "one-tag");
    }

    // 调用
    popSimpleDialog1("欢迎访问", "欢迎访问https://an.rustfisher.com\n入门的好选择~");

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/551339.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

日志收集机制和日志处理流程规范

本博客地址&#xff1a;https://security.blog.csdn.net/article/details/130792958 一、日志收集与处理流程 云原生平台中对日志提取收集以及分析处理的流程与传统日志处理模式大致是一样的&#xff0c;包括收集、ETL、索引、存储、检索、关联、可视化、分析、报告这9个步骤…

Leetcode 二叉树详解

二叉树 树的概念及基本术语见树与二叉树的基础知识 定义&#xff1a;一棵二叉树是结点的一个有限集合&#xff0c;该集合或者为空&#xff0c;或者是由一个根结点加上两棵分别称为左子树和右子树的、互不相交的二叉树组成。 特点&#xff1a;每个结点至多只有两棵子树&#xff…

Vivado综合属性系列之八 DIRECT_ENABLE DIRECT_RESET

目录 一、前言 二、DIRECT_ENABLE、DIRECT_RESET ​ ​2.1 属性说明 ​ ​2.2 工程代码 ​ ​2.3 综合结果 一、前言 在Vivado 2019之前的版本中&#xff0c;对于设计中触发器的使能端口和复位端口是会自动接地&#xff0c;如果需要接设计端口&#xff0c;如果要直连…

GitHub Copilot开发者酷游网址训练营

目标读者 已使用且【酷游网K͜W͜98典neт娜娜宝宝提供】想发挥GitHub Copilot所有潜能的使用者想知道GitHub Copilot未来展望的使用者想了解GitHub Copilot能力的开发者 简介 最近Open AI带起的新世代&#xff0c;热潮汹涌&#xff0c;一堆AI工具蜂拥而至(如:chatGPT和Midjo…

近期关于Transformer结构有潜力的改进方法总结

目录 0 引言1 Gated Linear Unit (GLU)1.1 思路 2 Gated Attention Unit (GAU)2.1 思路2.2 实验结论2.3 混合注意力 3 FlashAttention3.1 标准Attention的实现3.2 FlashAttention的实现针对目标1针对目标2 4 总结5 参考资料 0 引言 标准Transformer在最新的实际大模型中并没有…

C++STL算法篇之集合算法

CSTL算法篇之集合算法 集合算法set_union(并集)set_difference(差集)set_intersection(交集)set_symmetric_difference(对称差集) 集合算法 当然最好还是要包含 functional algorithm 这2个头文件 集合算法有4个函数 1.set_union 交集 2.set_difference 差集 3.set_intersectio…

安卓开发多选列表和回显已选择内容

问题背景 安卓日常开发和学习过程中&#xff0c;经常会碰到需要多选列表和显示已选择内容的场景&#xff0c;本文将介绍安卓实现多选列表和回显已选择内容的一种方案。 问题分析 话不多说&#xff0c;先上效果&#xff1a; 思路分析&#xff1a; 一个纵向列表显示待选择内…

多线程基础(二)CAS无锁优化/自旋锁/乐观锁、ABA问题

CAS &#xff08;Compare And Set&#xff09;比较并替换 上篇文章的锁问题解决&#xff0c;可以使用更高效的方法&#xff0c;使用AtomXXX类&#xff0c;AtomXXX类本身方法都是原子性的&#xff0c;但不能保证多个方法连续调用是原于性的。 import java.util.ArrayList; imp…

chatgpt赋能Python-pythoncd

Python介绍 Python是一种流行的高级编程语言&#xff0c;由Guido van Rossum于1989年开发。Python的设计目标是简单易学、易于阅读和编写&#xff0c;同时也是一种高效的语言&#xff0c;能够处理各种不同的任务。Python在Web开发、数据分析、人工智能和科学计算等领域得到广泛…

chatgpt赋能Python-pythoncalendar

PythonCalendar&#xff1a;Python中优秀的日期处理库 作为一门快速发展的编程语言&#xff0c;Python提供了许多优秀的库和工具&#xff0c;用于方便程序员进行各种各样的操作和处理。其中&#xff0c;日期处理是一个必不可少的模块。PythonCalendar库就是Python中优秀的日期…

ChatGPT 的 AskYourPDF 插件所需链接如何获取?

一、背景 目前 ChatGPT 主要有两款 PDF 对话插件&#xff0c;一个是 AskYourPDF 一个是 ChatWithPDF&#xff08;需 ChatGPT Plus&#xff09;&#xff0c;他们都可以实现给一个公共的PDF 链接&#xff0c;然后进行持续对话&#xff0c;对读论文&#xff0c;阅读 PDF 格式的文…

Godot引擎 4.0 文档 - 循序渐进教程 - 创建实例

本文为Google Translate英译中结果&#xff0c;DrGraph在此基础上加了一些校正。英文原版页面&#xff1a; Creating instances — Godot Engine (stable) documentation in English 创建实例 在前面的部分中&#xff0c;我们看到场景是以树结构组织的节点集合&#xff0c;以…

【中间件】通过 docker-compose 快速部署 Kafka 保姆级教程

文章目录 一、概述二、前期准备1&#xff09;部署 docker2&#xff09;部署 docker-compose 三、创建网络四、安装 Zookeeper五、Kafka 编排部署1&#xff09;下载 Kafka2&#xff09;配置3&#xff09;启动脚本 bootstrap.sh4&#xff09;构建镜像 Dockerfile5&#xff09;编排…

【VMware】搭建个人服务器

文章目录 准备工作三种网络模式Bridged(桥接模式)定义设置 NAT(网络地址转换模式)定义设置 Host-Only(仅主机模式)定义设置 搭建服务器网络模式的选择在VMWare的网络编辑器中设置转发端口查看宿主机的ip地址使用ssh连接工具进行连接 Mac笔记本跑虚拟机总感觉别扭&#xff0c;通…

浅谈一下“近期强势”这个指数

最近的行情,如果不理解退潮,那就意味着完全不理解情绪周期,也自然对大周期和小周期的概念了,这样一来无论你嘴上套用什么分歧、一致、修复都是徒劳的。 我说过我定义的新周期开始到结束,为什么我能定义一个很长的大周期?因为我有办法去观察赚钱效应。 如果我们都能理解…

【分布式锁】Redisson分布式锁底层原理

文章目录 前言原理分析Redisson实现Redis分布式锁的底层原理1.加锁机制2.锁互斥机制3. watch dog自动延期机制4.可重入加锁机制5.释放锁机制6.上述Redis分布式锁的缺点 前言 现在最流行的redis分布式锁就是Redisson了&#xff0c;来看看它的底层原理就了解redis是如何使用分布…

真香,聊聊 RocketMQ 5.0 的 POP 消费模式!

大家好&#xff0c;我是君哥。 大家都知道&#xff0c;RocketMQ 消费模式有 PULL 模式和 PUSH 模式&#xff0c;不过本质上都是 PULL 模式&#xff0c;而在实际使用时&#xff0c;一般使用 PUSH 模式。 不过&#xff0c;RocketMQ 的 PUSH 模式有明显的不足&#xff0c;主要体…

Unity 过场工具(Cutscene)设计(四) ——组件化设计

Unity 过场工具(Cutscene)设计&#xff08;四&#xff09; ——组件化设计 写到这一篇文章前就开始在考虑如何才能说清楚自己的设计思路&#xff0c;因为后续涉及到编辑器和Runtime框架的实际设计和实现过程&#xff0c;两者之间是互相有设计因果关系的。为了阐述自己的核心设计…

从0.5开始开发一个导购网站

提醒&#xff1a;文中没有具体如何修改的代码&#xff0c;只是提供了修改的思路。 为什么是从0.5开始呢&#xff1f; 因为这里借助了一个大佬的开源项目Springboot项目仿天猫商城: Springboot项目仿天猫商城 前台jsp页面 大佬的代码简洁&#xff0c;没有什么多余的功能&…

系统调用与API

系统调用介绍 什么是系统调用 为了让应用程序有能力访问系统资源&#xff0c;也为了让程序借助操作系统做一些由操作系统支持的行为&#xff0c;每个操作系统都会提供一套接口&#xff0c;以供应用程序使用。系统调用涵盖的功能很广&#xff0c;有程序运行所必需的支持&#xf…