2月5号
1. 目标
掌握Android UI设计之布局管理器
2. 学习笔记
2.1 UI 组件
- TextView
- Botton
- RadioBotton
- EditView
- ImageView
- RecyclerView
2.2 布局管理器
2.2.1 线性布局
最常用属性
属性 | 属性 | 属性 |
---|---|---|
android:id | android:layout_width | android:layout_height |
android:background | android:layout_margin | android:layout_padding |
android:orientation | android:gravity | android:layout_weight |
上机操作
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_margin="20dp"
tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="15521139529"
android:gravity="center"
></TextView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="密码"
android:layout_marginRight="10dp"
android:gravity="center"></TextView>
<EditText
android:layout_width="match_parent"
android:layout_height="match_parent"
android:hint="请填写微信密码"
android:inputType="textPassword"
android:maxLines="1"
></EditText>
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="用短信验证码登录"
android:textColor=" #7D9EC0"></TextView>
<Button
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="登录"
android:layout_marginTop="20dp"></Button>
</LinearLayout>
运行结果:
2.2.2 相对布局
最常用的属性
属性 | 作用 |
---|---|
android:layout_toLeftOf | 在谁的左边 |
android:layout_toRightOf | 在谁的右边 |
android:layout_above | 在谁的上边 |
android:layout_below | 在谁的下边 |
android:layout_alignBottom | 跟谁的底部对齐 |
android:layout_alignParentBottom | 跟父控件底部对齐 |
android:layout_centerInParent | 在父组件的中间 |
上机操作
代码:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="20dp"
android:id="@+id/relative"
tools:context=".MainActivity">
<TextView
android:id="@+id/text1"
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="15521139529"
android:gravity="center"></TextView>
<RelativeLayout
android:id="@+id/relative1"
android:layout_below="@+id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="密码"
android:layout_centerVertical="true">
</TextView>
<EditText
android:id="@+id/edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入微信密码"
android:layout_toRightOf="@+id/text2"
android:gravity="center"
android:inputType="textPassword"
></EditText>
</RelativeLayout>
<TextView
android:id="@+id/text3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="使用验证码登陆"
android:layout_below="@+id/relative1"
android:layout_alignParentLeft="true"
android:textColor=" #7D9EC0"
></TextView>
<Button
android:id="@+id/bottom1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="登录"
android:layout_marginTop="40dp"
android:padding="20dp"
android:layout_below="@id/text3">
</Button>
<RelativeLayout
android:id="@+id/bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_alignParentBottom="true"
android:gravity="center">
<TextView
android:id="@+id/text_bottom1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="找回密码"
android:textColor=" #7D9EC0"
android:layout_toLeftOf="@+id/text_bottom2"
></TextView>
<TextView
android:id="@+id/text_bottom2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:text="紧急冻结"
android:textColor=" #7D9EC0"></TextView>
<TextView
android:id="@+id/text_bottom3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="微信安全中心"
android:textColor=" #7D9EC0"
android:layout_toRightOf="@id/text_bottom2"></TextView>
</RelativeLayout>
</RelativeLayout>
运行结果:
2月6号
1. 学习目标
掌握一下组件的常见使用方法
- TextView
- Button
- EditView
2. 学习笔记
2.1 TextView
2.1.1 掌握的要点
- 设置文字的大小 颜色
- 文字太长显示不下时使用 …
- 文字 + icon
- 中划线 下划线
- 跑马灯效果
2.1.2 解决方法
- textColor 设置字体颜色, TextSize 设置字体大小, 注意这里使用的单位时sp
- maxLine 限制文字在一行中显示, ellipsize 设置缺省的显示格式, 选择 end 就好
- drawableRight 即可在文字右边添加一个图片, 得事先准备好图片, 再添加一个点击
监听事件即可实现一些想 筛选 的功能 - 添加中划线和下划线在java文件中设置, 代码如下:
private TextView mTv4;
mTv4 = findViewById(R.id.tv_4);
mTv4.getPaint().setFlags(Paint.STRIKE_THRU_TEXT_FLAG); // 添加中划线
mTv4.getPaint().setAntiAlias(true); // 去除锯齿
private TextView mTv5;
mTv5 = findViewById(R.id.tv_5);
mTv5.getPaint().setFlags(Paint.UNDERLINE_TEXT_FLAG); // 添加下划线
mTv5.getPaint().setAntiAlias(true); // 去除锯齿
- 跑马灯的实现, 要靠以下几个属性来设置
android:singleLine="true"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:focusable="true"
android:focusableInTouchMode="true"
2.2 Button
2.2.1 要点
- 按钮大小 颜色 的设置
- 自定义按钮背景
- 自定义按压效果
- 点击事件
2.2.2 解决方法
- 同TextView ,因为Button继承于TextView
- 通过新建drawable resouce file 来自定义背景, 常见的有直角 圆角 描边, 代码如下:
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid
android:color="#FFCC00"/> // 填充颜色
<corners
android:radius="10dp"/> // 圆角
</shape>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="2dp"
android:color="#FFCC00"/> // 描边
<corners
android:radius="10dp"/> //圆角
</shape>
- 同理, 自定义按压效果, 代码如下:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"> // 按下去
<shape>
<solid android:color="#00BB25"/> // 填充颜色
<corners android:radius="10dp"/> // 圆角
</shape>
</item>
<item android:state_pressed="false"> // 没有按下去
<shape>
<solid android:color="#00DD2C"/>
<corners android:radius="10dp"/>
</shape>
</item>
</selector>
- 方法一: 在布局文件中添加 onClick 属性, 其值为自定义的一个方法名字, 然后在 Activity 中定义并实现该方法, 代码如下:
android:onClick="showToast"
public void showToast(View view) {
Toast.makeText(this, "点击了一下按钮4", Toast.LENGTH_LONG).show();
}
方法二 (推荐) : 直接在 Activity 中获取控件, 新建监听事件来实现, 代码如下:
private Button mBtn3;
mBtn3 = findViewById(R.id.btn_3);
mBtn3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(ButtonActivity.this, "点击了一下按钮3", Toast.LENGTH_LONG).show();
}
});
2.3 EditText
2.3.1 要点
- 常用属性
- 监听事件
2.3.2 解决方法
- 属性
常用属性 | 作用 |
---|---|
hint | 提示语 |
inputType | 输入类型 |
- 监听事件, 监听 text 值改变前, 中, 后
mEditText1 = findViewById(R.id.et_1);
mEditText1.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Log.d("useName", s.toString());
}
@Override
public void afterTextChanged(Editable s) {
}
});
2月7号
1. 学习目标
掌握以下控件的常见用法:
- RadioButton
- CheckBox
- ImageView
- ListView
- GridView
- ScrollView
2. 学习笔记
2.1 RadioButton 和 RadioGroup
2.1.1 要点
- 常用属性
- 自定义样式
- 监听事件
2.1.2 解决方法
- 如果是一组, 则需要使用 RadioGroud 包含多个 * RadioButton* 来实现多选一, 常见属性:
属性 | 作用 |
---|---|
checked | 默认情况下是否选中 |
orientation | 选项摆放方向, 水平或者垂直 |
button | 设置勾选按钮的样式, 可以设置为 @null 来隐藏, 然后使用自定义的图案 |
- 同其他自定义样式一样, 新建 drawable resouce file , 设置 background 的值为新建文件名就好, 代码如下:
android:button="@null"
android:background="@drawable/rb_rb_2"
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true">
<shape>
<solid android:color="#00BB25"/>
<corners android:radius="10dp"/>
</shape>
</item>
<item android:state_checked="false">
<shape>
<stroke
android:width="2dp"
android:color="#00BB25"/>
<corners android:radius="10dp"/>
</shape>
</item>
</selector>
- 代码如下:
private RadioGroup mRg1;
mRg1 = findViewById(R.id.rg_1);
mRg1.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
RadioButton radioButton = group.findViewById(checkedId);
Toast.makeText(RadioButtonActivity.this, radioButton.getText(), Toast.LENGTH_SHORT).show();
}
});
2.2 CheckBox
多个 CheckBox 就是一组选择几项, 即复选的 RadioGroup
2.1.1 要点
- 常用属性
- 自定义样式
- 监听事件
2.1.2 解决方法
- 同 RaidoBox
- 同 RaidoBox , 代码如下:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:drawable="@drawable/checkbox_checked"/>
<item android:state_checked="false" android:drawable="@drawable/checkmark_unchecked"/>
</selector>
- 监听的方法也跟 RadioGrounp 差不多, 代码如下
private CheckBox mCb5, mCb6;
mCb5 = findViewById(R.id.cb_21);
mCb6 = findViewById(R.id.cb_22);
mCb5.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Toast.makeText(CheckBoxActivity.this, isChecked?"checked":"unchecked", Toast.LENGTH_SHORT).show();
}
});
mCb6.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Toast.makeText(CheckBoxActivity.this, isChecked?"checked":"unchecked", Toast.LENGTH_SHORT).show();
}
});
2.3 ImageView
2.3.1 要点
- 常用属性
- 加载网络图片
2.3.2 方法
- 常见属性
属性 | 作用 |
---|---|
src | 图片资源的位置 |
scaleType | 图片适配视图的类型 |
scaleType值 | 效果 |
---|---|
fitXY | 拉伸长和宽, 使图片撑满整个 ImageView |
fitCenter | 等比缩放, 是图片完整展示在 ImageView 中 |
centerCrop | 等比缩放, 使图片撑满整个 ImageView |
- 加载开源包, 直接去 GitHub 官网搜索所需要的包, 按照其说明指引进行操作, 就可以调用相关功能了, 代码如下
// 加载开源包, 将对应的内容复制粘贴到 *build.gradle(Moudle:app)* 的相应位置, 然后点击同步即可
repositories {
mavenCentral()
google()
}
dependencies {
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
}
// 使用开源包, 加载网络图片
private ImageView mIv4;
mIv4 = findViewById(R.id.iv_4);
Glide.with(this).load("https://i0.hdslb.com/bfs/archive/058056424b94c3ff8c1facc940f48ce3bfe423a5.jpg@1100w_484h_1c_100q.jpg").into(mIv4);
2.4 ListView
2.4.1 常用xml属性
属性 | 作用 |
---|---|
listSelector | 设置选择的效果, 可以自定义drawable文件来实现点击效果 |
divider | 设置item间的分割线的样式 |
dividerHeight | 设置分割线的宽度 |
2.4.2 列表的生成
- 新建一个布局文件, 设计一个 item 的布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp">
<ImageView
android:id="@+id/iv"
android:layout_width="150dp"
android:layout_height="100dp"
android:background="@color/colorBlack"
android:scaleType="fitXY"/>
<LinearLayout
android:id="@+id/ll"
android:layout_width="match_parent"
android:layout_height="100dp"
android:orientation="vertical"
android:paddingLeft="10dp">
<TextView
android:id="@+id/tv_tittle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="橡皮擦"
android:textSize="20sp"
android:textColor="@color/colorBlack"/>
<TextView
android:id="@+id/tv_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="2020-02-07"
android:textSize="18sp"
android:textColor="@color/colorGray"
android:layout_marginTop="5dp"/>
<TextView
android:id="@+id/tv_details"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="这是介绍, 橡皮擦擦铅笔迹!"
android:textSize="19sp"
android:layout_marginTop="5dp"
android:textColor="@color/colorGray"/>
</LinearLayout>
</LinearLayout>
- 这里的内容需要了解一下几个知识
- 菜鸟教程: 加载布局的系统服务 LayoutInflater
- 需要实现 BaseAdapter 类的 getView() 方法
- setTag() 和 getTag() 方法
public class MyListAdapter extends BaseAdapter {
private Context mContext;
private LayoutInflater mLayoutInflater;
public MyListAdapter(Context context) {
this.mContext = context;
mLayoutInflater = LayoutInflater.from(context); // 获取布局填充器
}
@Override
public int getCount() {
return 10;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
static class ViewHolder {
public ImageView imageView;
public TextView tvTittle, tvDate, tvDetails;
}
@Override
// 列表每一行长什么样子, 这个方法可以得到
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if (convertView == null) { // 如果当前 item 是空的, 则新建一个convertView, 更新viewHolder, 并放到缓存区
// convertView 是一个item View, 将R.layout.layout_list_item填充到一个view得到convertView
convertView = mLayoutInflater.inflate(R.layout.layout_list_item, null);
// viewHolder 是管理一个 item 上的所有控件的类对象
viewHolder = new ViewHolder();
viewHolder.imageView = convertView.findViewById(R.id.iv);
viewHolder.tvTittle = convertView.findViewById(R.id.tv_tittle);
viewHolder.tvDate = convertView.findViewById(R.id.tv_date);
viewHolder.tvDetails = convertView.findViewById(R.id.tv_details);
// 将 viewHolder 放到缓存区
convertView.setTag(viewHolder);
} else { // 如果当前 item 不是空的, 则直接从缓存区取来就好
viewHolder = (ViewHolder)convertView.getTag();
}
// 给控件赋值, 会覆盖 xml 文件中的初始值
viewHolder.tvTittle.setText("这是标题");
viewHolder.tvDate.setText("2022-02-22");
viewHolder.tvDetails.setText("这是内容喔!");
Glide.with(mContext).load("https://i0.hdslb.com/bfs/archive/058056424b94c3ff8c1facc940f48ce3bfe423a5.jpg@1100w_484h_1c_100q.jpg").into(viewHolder.imageView);
return convertView;
}
}
private ListView mLv1;
mLv1 = findViewById(R.id.lv_1);
mLv1.setAdapter(new MyListAdapter(ListViewActivity.this));
2.4.3 监听事件
- 点击
mLv1.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(ListViewActivity.this, "position" + position, Toast.LENGTH_SHORT).show();
}
});
- 长按
mLv1.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(ListViewActivity.this, "position" + position, Toast.LENGTH_SHORT).show();
return true;
}
});
2.5 GridView
2.5.1 常用 xml 属性
也有 ListView 的许多属性, 还有以下这些:
属性 | 作用 |
---|---|
numColumns | 多少列 |
horizontalSpacing | 水平间距 |
verticalSpacing | 竖直间距 |
2.5.2 Adapter
同 ListView , 代码如下:
- 新建一个布局文件, 设计一个 item 的布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp">
<ImageView
android:id="@+id/iv_image"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="@color/colorPink"
android:scaleType="centerCrop"/>
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="name"
android:gravity="center"/>
</LinearLayout>
- 自定义 MyGridViewAdapter 类
public class MyGridViewAdapter extends BaseAdapter {
private Context mContext;
private LayoutInflater mLayoutInflater;
public MyGridViewAdapter(Context context){
this.mContext = context;
mLayoutInflater = LayoutInflater.from(context);
}
static class ViewHolder{
public ImageView mImageView;
public TextView mTextView;
}
@Override
public int getCount() {
return 10;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.layout_grid_item, null);
viewHolder = new ViewHolder();
viewHolder.mImageView = convertView.findViewById(R.id.iv_image);
viewHolder.mTextView = convertView.findViewById(R.id.tv_name);
convertView.setTag(viewHolder);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.mTextView.setText("JoJo");
Glide.with(mContext).load("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1581092772754&di=252dc3a97008701c0c654df1cb070f42&imgtype=0&src=http%3A%2F%2Fi2.hdslb.com%2Fbfs%2Farchive%2F23cd383165745a6fdc4ddc06ecc31e5cee91f91c.jpg").into(viewHolder.mImageView);
return convertView;
}
}
private GridView mGv1
mGv1 = findViewById(R.id.gv);
mGv1.setAdapter(new MyGridViewAdapter(GridViewActivity.this));
- 监听事件
同 ListView
// 单次点击
mGv1.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(GridViewActivity.this, "点击了图片" + position, Toast.LENGTH_SHORT).show();
}
});
// 长按
mGv1.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(GridViewActivity.this, "长按了图片" + position, Toast.LENGTH_SHORT).show();
return true;
}
});
2.6 ScrollView
当控件太多, 手机屏幕放不下时, 就可以使用 ScrollView 来实现滚动功能
2.6.1 使用
将需要滚动的布局放在 ScrollView 里面就好, 注意里面只能放一个元素, 竖直滚动用 ScrollView , 水平滚动用 HorizontalScrollView
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ScrollViewActivity">
<LinearLayout
android:id="@+id/sv_ll_1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_margin="5dp">
<TextView
android:layout_width="match_parent"
android:layout_height="150dp"
android:text="\(@^0^@)/"
android:textSize="30sp"
android:gravity="center"
android:background="@color/colorPink"/>
<TextView
android:layout_width="match_parent"
android:layout_height="150dp"
android:text="\(@^0^@)/"
android:textSize="30sp"
android:gravity="center"
android:background="@color/colorPink"
android:layout_marginTop="10dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="150dp"
android:text="\(@^0^@)/"
android:textSize="30sp"
android:gravity="center"
android:background="@color/colorPink"
android:layout_marginTop="10dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="150dp"
android:text="\(@^0^@)/"
android:textSize="30sp"
android:gravity="center"
android:background="@color/colorPink"
android:layout_marginTop="10dp"/>
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:layout_width="200dp"
android:layout_height="150dp"
android:text="\(@^0^@)/"
android:textSize="30sp"
android:gravity="center"
android:background="@color/colorPink"
android:layout_marginTop="10dp"/>
<TextView
android:layout_width="200dp"
android:layout_height="150dp"
android:layout_marginTop="10dp"
android:background="@color/colorPink"
android:gravity="center"
android:text="\(@^0^@)/"
android:textSize="30sp"
android:layout_marginLeft="10dp"/>
<TextView
android:layout_width="200dp"
android:layout_height="150dp"
android:text="\(@^0^@)/"
android:textSize="30sp"
android:gravity="center"
android:background="@color/colorPink"
android:layout_marginTop="10dp"
android:layout_marginLeft="10dp"/>
</LinearLayout>
</HorizontalScrollView>
</LinearLayout>
</ScrollView>
2月8号
1. 学习目标
RecycleView
2. 学习笔记
2.1 RecyclerView
2.1.1 简述 RecyclerView
- 可以它被用来代替ListView和GridView, 灵活地实现大数据集的展示。
- 有列表、网格和瀑布流等形式。
- viewHoldwer能够实现多元的item,例如微信聊天界面有文字、图片和地址信息等多元item。
2.1.2 导入 Recyclerview 库的依赖
2.1.3 实现竖直列表功能
- 设置 布局管理器:有三种布局管理器,这里用 LinearLayoutManager
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(HorRecyclerViewActivity.this); // 创建布局管理器对象
mRv.setLayoutManager(linearLayoutManager); // 为RecyclerView控件设置布局管理器
- 设置 适配器
mRvHor.setAdapter(new HorAdapter(HorRecyclerViewActivity.this, new HorAdapter.OnItemClickListener() {
@Override
public void onClick(int pos) {
Toast.makeText(HorRecyclerViewActivity.this, "点击了" + pos, Toast.LENGTH_SHORT).show();
}
}));
2.1.3.1 具体实现
- 布局文件准备:一个 activity 的布局文件,还有一个 item 布局文件
// activity的布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".recyclerview.RecyclerViewActivity"
android:layout_margin="10dp">
<Button
android:id="@+id/btn_linear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="列表视图"/>
</LinearLayout>
// item布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_tittle"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="20sp"
android:textColor="@color/colorBlack"
android:gravity="center"
android:background="@color/colorWhite"/>
</LinearLayout>
- 实现适配器
public class LinearAdapter extends RecyclerView.Adapter<LinearAdapter.LinearViewHolder> {
private Context mContext;
public LinearAdapter(Context context) {
this.mContext = context;
}
@NonNull
@Override
/**
* 创建 ViewHolder, 是用来实现复用功能的
*/
public LinearAdapter.LinearViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new LinearViewHolder(LayoutInflater.from(mContext).inflate(R.layout.layout_linear_item, parent, false));
}
@Override
/**
* 是用来设置数据的,当item划入屏幕时,就会调用此方法,来改变数据
*/
public void onBindViewHolder(@NonNull LinearAdapter.LinearViewHolder holder, final int position) {
holder.mTv.setText("设置holder的值");
}
@Override
/**
* 是用来获取item个数的,实际开发是要获取List的长度
*/
public int getItemCount() {
return 30;
}
/**
* 自定义ViewHolder的类型,并将此类型代替所有的泛型
*/
class LinearViewHolder extends RecyclerView.ViewHolder {
private TextView mTv;
public LinearViewHolder(@NonNull View itemView) {
super(itemView);
mTv = itemView.findViewById(R.id.tv_tittle);
}
}
}
- 使用适配器
public class LinearRecyclerViewActivity extends AppCompatActivity {
private RecyclerView mRvMain;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_linear_recycler_view);
mRvMain = findViewById(R.id.rv_main);
mRvMain.setLayoutManager(new LinearLayoutManager(LinearRecyclerViewActivity.this)); // 设置布局管理器, 有三种线性、表格和瀑布流
mRvMain.setAdapter(new LinearAdapter(LinearRecyclerViewActivity.this);
}
}
2.1.3.2 实现分割线
使用 addItemDecoration 来实现分割线
public class LinearRecyclerViewActivity extends AppCompatActivity {
private RecyclerView mRvMain;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_linear_recycler_view);
mRvMain = findViewById(R.id.rv_main);
mRvMain.setLayoutManager(new LinearLayoutManager(LinearRecyclerViewActivity.this)); // 设置布局管理器, 有三种线性、表格和瀑布流
mRvMain.setAdapter(new LinearAdapter(LinearRecyclerViewActivity.thi);
mRvMain.addItemDecoration(new MyDecoration());
}
/**
* 实现分割线的作用
*/
class MyDecoration extends RecyclerView.ItemDecoration {
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
outRect.set(0, 0, 0, getResources().getDimensionPixelOffset(R.dimen.dividerHeight));
}
}
}
2.1.3.3 监听事件
- 方法一:直接在 onBindViewHolder() 方法中新建监听事件即可。
@Override
/**
* 是用来设置数据的,当item划入屏幕时,就会调用此方法,来改变数据或设置监听(第一种方法)
*/
public void onBindViewHolder(@NonNull LinearAdapter.LinearViewHolder holder, final int position) {
holder.mTv.setText("设置holder的值");
holder.mTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(mContext, "点击了" + position, Toast.LENGTH_SHORT).show();
}
});
}
- 方法二:使用接口,在自定义的适配器中定义监听事件的接口,实例化适配器时重写接口的点击方法,实现监听事件。
// 适配器的代码
public class LinearAdapter extends RecyclerView.Adapter<LinearAdapter.LinearViewHolder> {
private Context mContext;
private OnItemClickListener mOnItemClickListener; // 声明接口
public LinearAdapter(Context context, OnItemClickListener onItemClickListener) {
this.mContext = context;
this.mOnItemClickListener = onItemClickListener; // 初始化接口
}
@NonNull
@Override
/**
* 创建 ViewHolder, 是用来复用的
*/
public LinearAdapter.LinearViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new LinearViewHolder(LayoutInflater.from(mContext).inflate(R.layout.layout_linear_item, parent, false));
}
@Override
/**
* 是用来设置数据的,当item划入屏幕时,就会调用此方法,来改变数据或设置监听(第一种方法)
*/
public void onBindViewHolder(@NonNull LinearAdapter.LinearViewHolder holder, final int position) {
holder.mTv.setText("设置holder的值");
holder.mTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 接口方法的调用,实例化后重写该方法,即最终重写后的方法在此处调用
mOnItemClickListener.onClick(position);
}
});
}
@Override
/**
* 是用来获取item个数的,实际开发是要获取List的长度
*/
public int getItemCount() {
return 30;
}
/**
* 自定义ViewHolder的类型,并将此类型代替所有的泛型
*/
class LinearViewHolder extends RecyclerView.ViewHolder {
private TextView mTv;
public LinearViewHolder(@NonNull View itemView) {
super(itemView);
mTv = itemView.findViewById(R.id.tv_tittle);
}
}
/**
* 写一个接口实现第二种设置监听的方法
* 即实例化这个类,就得实现这个接口,重写方法,而该方法又是在onBindViewHolder()方法中调用的。
* 如此一来,只要重写方法中添加一个监听事件就可以实现,但item出现在屏幕时,就可以为该item添加一个监听事件。
*/
public interface OnItemClickListener {
void onClick(int pos);
}
}
// activity的代码
public class LinearRecyclerViewActivity extends AppCompatActivity {
private RecyclerView mRvMain;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_linear_recycler_view);
mRvMain = findViewById(R.id.rv_main);
mRvMain.setLayoutManager(new LinearLayoutManager(LinearRecyclerViewActivity.this)); // 设置布局管理器, 有三种线性、表格和瀑布流
mRvMain.setAdapter(new LinearAdapter(LinearRecyclerViewActivity.this, new LinearAdapter.OnItemClickListener() {
@Override
public void onClick(int pos) {
Toast.makeText(LinearRecyclerViewActivity.this, "点击了" + pos, Toast.LENGTH_SHORT).show();
}
}));
}
}
2.1.4 水平滚动
只需要设置线性布局的方向就好了
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(HorRecyclerViewActivity.this);
linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); // 设置滚动方向
mRvHor.setLayoutManager(linearLayoutManager);
mRvHor.setAdapter(new HorAdapter(HorRecyclerViewActivity.this, new HorAdapter.OnItemClickListener() {
@Override
public void onClick(int pos) {
Toast.makeText(HorRecyclerViewActivity.this, "点击了" + pos, Toast.LENGTH_SHORT).show();
}
}));
2.1.5 网格布局
得使用表格布局管理器 GridLayoutManager
new GridLayoutManager(GridRecyclerViewActivity.this, 3);
public class GridRecyclerViewActivity extends AppCompatActivity {
private RecyclerView mRvGrid;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_grid_recycler_view);
mRvGrid = findViewById(R.id.rv_grid);
GridLayoutManager gridLayoutManager = new GridLayoutManager(GridRecyclerViewActivity.this, 3); // 3列
mRvGrid.setLayoutManager(gridLayoutManager);
mRvGrid.setAdapter(new GridAdapter(GridRecyclerViewActivity.this, new GridAdapter.OnItemClickListener() {
@Override
public void onClick(int pos) {
Toast.makeText(GridRecyclerViewActivity.this, "点击了" + pos, Toast.LENGTH_SHORT).show();
}
}));
}
}
2.1.5 瀑布流
得使用表格布局管理器 StaggeredGridLayoutManager
new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);
public class StaggeredRecyclerViewActivity extends AppCompatActivity {
private RecyclerView mRvSta;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_staggered_recycler_view);
mRvSta = findViewById(R.id.rv_staggered);
StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);
mRvSta.setLayoutManager(staggeredGridLayoutManager);
mRvSta.setAdapter(new StaggeredAdapter(StaggeredRecyclerViewActivity.this, new StaggeredAdapter.OnItemClickListener() {
@Override
public void onClick(int pos) {
Toast.makeText(StaggeredRecyclerViewActivity.this, "点击了" + pos, Toast.LENGTH_SHORT).show();
}
}));
mRvSta.addItemDecoration(new MyItemDecoration());
}
}
2.1.6 实现多种item展示
就是自定义Adapter的ViewHolder要求多种类型,可以通过重写getItemViewType(int position)方法,根据不同的信息返回不同的item类型。接下来ViewHolder的创建和onBindViewHolder方法中的具体操作都根据item类型来操作。
public class MulVHAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context mContext;
private OnItemClickListener mOnItemClickListener;
public MulVHAdapter(Context context, OnItemClickListener onItemClickListener) {
this.mContext = context;
this.mOnItemClickListener = onItemClickListener;
}
@Override
/**
* 根据iitem的位置,判断item的类型
* return 0: 奇数, item是Text类型
* return 1: 偶数,item是Iamge类型
*/
public int getItemViewType(int position) {
int flag;
if(position % 2 == 0){
flag = 1;
} else {
flag = 0;
}
return flag;
}
@NonNull
@Override
/**
* 根据item的类型,创建对应的ViewHolder
*/
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
RecyclerView.ViewHolder viewHolder = null;
if(viewType == 0) { // Text类型
viewHolder = new MyViewHolderText(LayoutInflater.from(mContext).inflate(R.layout.layout_mvh_text_item, parent, false));
} else { // Image类型
viewHolder = new MyViewHolderImage(LayoutInflater.from(mContext).inflate(R.layout.layout_mvh_image_item, parent, false));
}
return viewHolder;
}
@Override
/**
* 根据item的类型,进行不同的操作
*/
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, final int position) {
if(getItemViewType(position) == 0) {
((MyViewHolderText)holder).mTv.setText("我是文本");
((MyViewHolderText)holder).mTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnItemClickListener.onClick(position);
}
});
} else {
((MyViewHolderImage)holder).mIv.setImageResource(R.drawable.pikaqiu);
((MyViewHolderImage)holder).mIv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnItemClickListener.onClick(position);
}
});
}
}
@Override
/**
* 是用来获取item个数的,实际开发是要获取List的长度
*/
public int getItemCount() {
return 30;
}
/**
* 自定义ViewHolder的类型1, Text类型
*/
class MyViewHolderText extends RecyclerView.ViewHolder {
private TextView mTv;
public MyViewHolderText(@NonNull View itemView) {
super(itemView);
mTv = itemView.findViewById(R.id.tv_mvh);
}
}
/**
* 自定义ViewHolder的类型2,Image类型
*/
class MyViewHolderImage extends RecyclerView.ViewHolder {
private ImageView mIv;
public MyViewHolderImage(@NonNull View itemView) {
super(itemView);
mIv = itemView.findViewById(R.id.iv_mvh);
}
}
/**
* 写一个接口实现第二种设置监听的方法
* 即实例化这个类,就得实现这个接口。如此一来,就可以
*/
public interface OnItemClickListener {
void onClick(int pos);
}
}
2月9号
1. 学习目标
- WebView
- Toast
- AlertDialog
2. 学习笔记
2.1 WebView
加载网页
- 加载 URL(网络或者本地assets文件夹下的html文件)
- 加载 HTML 代码
- Native 和 JavaScript 的相互调用
2.1.1 加载本地html文件
需要在main文件夹下新建一个assete文件夹,把html文件放在这个文件夹下,使用以下代码进行加载。
mWvMain.loadUrl("file:///android_asset/test.html");
2.1.2 加载网页
分两步走:
- 设置JavaScript支持:
mWvMain.getSettings().setJavaScriptEnabled(true);
- 调用加载方法:
mWvMain.loadUrl("https://m.baidu.com");
2.1.3 WebViewClient
用于处理各种通知、请求事件。为一个webView设置 WebViewClient mWvMain.setWebViewClient(new MyWebViewClient());
其中,MyWebViewClient() 是自定义的继承于 WebViewClient 的类,重写以下方法:
- 使加载的URL界面保留在当前的 WebView
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return false;
}
- 监听界面加载开始时
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
Log.d("WebView", "Page Started");
}
- 监听界面加载结束时
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
Log.d("WebView", "Page Finished");
}
2.1.4 WebChromClient
同 WebViewClient , 只不过 WebChromClient 是处理对话框、网站图标、网站title、加载进度等。
为一个 WebView 设置一个WebChromClient: mWvMain.setWebChromeClient(new MyWebChromeClient());
public class MyWebChromeClient extends WebChromeClient {
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
}
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
setTitle(title);
}
}
2.1.4 浏览中的返回键
进入URL界面时,若不对返回键进行设置,则返回键会直接退出URL界面。实现返回键为回到上一个URL界面,秩序重写监听方法 onKeyDown(int keyCode, KeyEvent event)
即可,代码如下:
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK && mWvMain.canGoBack()) {
mWvMain.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
2.1.5 综合代码
public class WebViewActivity extends AppCompatActivity {
private WebView mWvMain;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web_view);
mWvMain = findViewById(R.id.wv);
// 加载本地html文件
// mWvMain.loadUrl("file:///android_asset/test.html");
// 加载网络URL
mWvMain.getSettings().setJavaScriptEnabled(true); // 设置支持
mWvMain.setWebViewClient(new MyWebViewClient()); // 处理各种通知、请求事件
mWvMain.setWebChromeClient(new MyWebChromeClient()); // 处理对话框、网站图标、网站title、加载进度等
mWvMain.loadUrl("https://m.baidu.com"); //
}
public class MyWebViewClient extends WebViewClient {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
/**
* 返回true终止加载URL,返回false在WebView中加载URL
*/
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return false;
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
Log.d("WebView", "Page Started");
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
Log.d("WebView", "Page Finished");
}
}
public class MyWebChromeClient extends WebChromeClient {
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
}
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
setTitle(title);
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK && mWvMain.canGoBack()) {
mWvMain.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
}
2.2 Toast
是一个消息弹窗提示组件
- 默认调用:
Toast.makeText(getApplicationContext(), "Toast", Toast.LENGTH_SHORT).show();
- 调整位置:
toastCenter.setGravity(Gravity.CENTER, 0, 0);
- 自定义图案:
toastCustom.setView(view);
switch (v.getId()){
case R.id.btn_toast_1: // 默认情况
Toast.makeText(getApplicationContext(), "Toast", Toast.LENGTH_SHORT).show();
break;
case R.id.btn_toast_2: // 调整位置
Toast toastCenter = Toast.makeText(getApplicationContext(), "Toast", Toast.LENGTH_SHORT);
toastCenter.setGravity(Gravity.CENTER, 0, 0);
toastCenter.show(); ;
case R.id.btn_toast_3: // 自定义图案
Toast toastCustom = new Toast(ToastActivity.this);
View view = LayoutInflater.from(ToastActivity.this).inflate(R.layout.layout_toast_item, null);
ImageView imageView = view.findViewById(R.id.iv_toast);
TextView textView = view.findViewById(R.id.tv_toast);
imageView.setImageResource(R.drawable.icon_smile);
textView.setText("我是一个Toast");
toastCustom.setView(view);
toastCustom.setDuration(Toast.LENGTH_SHORT);
toastCustom.show();
break;
}
2.3 AlertDialog
弹出一个消息对话框. 使用构建器来管理, 具体方法可以看构建器里面的方法介绍, 新建构建器: AlertDialog.Builder builder5 = new AlertDialog.Builder(DialogActivity.this);
实现5种情况的对话框
switch (v.getId()) {
case R.id.btn_dialog_1:
AlertDialog.Builder builder1 = new AlertDialog.Builder(DialogActivity.this);
builder1.setTitle("请回答").setMessage("我帅不帅?").setIcon(R.drawable.icon_smile)
.setPositiveButton("帅呆了", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(DialogActivity.this, "有眼光", Toast.LENGTH_SHORT).show();
}
}).setNegativeButton("一般", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(DialogActivity.this, "Fuck", Toast.LENGTH_SHORT).show();
}
}).setNeutralButton("还行", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(DialogActivity.this, "你再想想", Toast.LENGTH_SHORT).show();
}
}).show();
break;
case R.id.btn_dialog_2:
final String[] array2 = new String[]{"boy", "girl"};
AlertDialog.Builder builder2 = new AlertDialog.Builder(DialogActivity.this);
builder2.setTitle("What's your sex?").setItems(array2, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(DialogActivity.this, array2[which], Toast.LENGTH_SHORT).show();
}
}).show();
break;
case R.id.btn_dialog_3: // 单选
final String[] array3 = new String[]{"boy", "girl"};
AlertDialog.Builder builder3 = new AlertDialog.Builder(DialogActivity.this);
builder3.setTitle("What's your sex?").setSingleChoiceItems(array3, 1, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(DialogActivity.this, array3[which], Toast.LENGTH_SHORT).show();
}
}).show();
break;
case R.id.btn_dialog_4: // 多选
final String[] array4 = new String[]{"boy", "girl", "I don't know!"};
boolean[] isSelected = new boolean[]{false, false, true};
AlertDialog.Builder builder4 = new AlertDialog.Builder(DialogActivity.this);
builder4.setTitle("What's your sex?").setMultiChoiceItems(array4, isSelected, new DialogInterface.OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
Toast.makeText(DialogActivity.this, array4[which], Toast.LENGTH_SHORT).show();
}
}).setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(DialogActivity.this, "确定", Toast.LENGTH_SHORT).show();
}
}).setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(DialogActivity.this, "取消", Toast.LENGTH_SHORT).show();
}
}).show();
break;
case R.id.btn_dialog_5: // 自定义View
AlertDialog.Builder builder5 = new AlertDialog.Builder(DialogActivity.this);
View view = LayoutInflater.from(DialogActivity.this).inflate(R.layout.layout_dialog, null);
builder5.setView(view).setTitle("请先登录").show();
break;
}
2月10号
1. 学习任务
- ProgressBar 和 ProgressDialog
- 自定义 Dialog
- PopupWindow
2. 学习笔记
2.1 PressingDialog 和 PressingBar
前者是进度条, 后者是进度对话框, 在API26就已经弃用了, 这里不学了, 也挺简单的
下面列举出几种样式的进度条, 也可以 自定义样式, 这里不说自定义样式了, 需要时候再搜索就好.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_margin="10dp">
<ProgressBar
android:id="@+id/rb_def"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="visible"/>
<ProgressBar
android:id="@+id/rb_style_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
style="@android:style/Widget.ProgressBar"
android:layout_marginTop="10dp"/>
<ProgressBar
android:id="@+id/rb_style_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@android:style/Widget.ProgressBar.Horizontal"
android:progress="10"
android:secondaryProgress="30"
android:layout_marginTop="10dp"/>
<ProgressBar
android:id="@+id/rb_style_3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@android:style/Widget.Holo.ProgressBar.Horizontal"
android:progress="20"
android:secondaryProgress="30"
android:layout_marginTop="10dp"/>
<ProgressBar
android:id="@+id/rb_style_4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@android:style/Widget.DeviceDefault.ProgressBar.Horizontal"
android:progress="20"
android:secondaryProgress="30"
android:layout_marginTop="10dp"/>
</LinearLayout>
写都写了, 还是将代码附上来吧
mBtnProgressDialog1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ProgressDialog progressDialog = new ProgressDialog(ProgressActivity.this);
progressDialog.setTitle("请稍等");
progressDialog.setMessage("正在加载");
progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
Toast.makeText(ProgressActivity.this, "加载完成", Toast.LENGTH_SHORT).show();
}
});
progressDialog.setCancelable(false); // 不允许返回, 当加载完成时才能可以调用progressDialog.setCancelable(true); 来取消加载
progressDialog.setCancelable(true);
progressDialog.show();
}
});
2.2 自定义 Dialog
- 自定义一个类 CustomDialog 继承于 Dialog
- 重写 onCreate() 方法, 对应上一个 CustomDialog 布局文件, 获取布局文件上的各个控件, 并为每一个控件设置存储数据的变量, 并写出它们的 setter() 和 getter() 方法.
- 让这个自定义的类 CustomDialog 实现接口 View.OnClickListener , 为相关的控件设置点击事件监听, 重写 onClick() 方法, 在 onClick() 方法中调用在 CustomDialog 中自定义的接口的方法, 如此一来, 就可以用 CustomDialog 的实例传入自定义接口, 同时重写自定义接口的方法
自定义类 CustomDialog 代码如下:
public class CustomDialog extends Dialog implements View.OnClickListener{
private TextView mTvTittle, mTvMassage, mTvPositive, mTvNegative;
private String tittle, massage, positive, negative;
private IOnNegativeListener negativeListener;
private IOnPositiveListener positiveListener;
public CustomDialog(@NonNull Context context) {
super(context);
}
public CustomDialog(@NonNull Context context, int themeResId) {
super(context, themeResId);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_custom_dialog);
mTvTittle = findViewById(R.id.tv_tittle);
mTvMassage = findViewById(R.id.tv_massage);
mTvPositive = findViewById(R.id.tv_positive);
mTvNegative = findViewById(R.id.tv_negative);
if (!TextUtils.isEmpty(tittle)) {
mTvTittle.setText(tittle);
}
if (!TextUtils.isEmpty(massage)) {
mTvMassage.setText(massage);
}
if (!TextUtils.isEmpty(positive)) {
mTvPositive.setText(positive);
}
if (!TextUtils.isEmpty(negative)) {
mTvNegative.setText(negative);
}
mTvPositive.setOnClickListener(this);
mTvNegative.setOnClickListener(this);
}
public String getTittle() {
return tittle;
}
public CustomDialog setTittle(String tittle) {
this.tittle = tittle;
return this;
}
public String getMassage() {
return massage;
}
public CustomDialog setMassage(String massage) {
this.massage = massage;
return this;
}
public String getPositive() {
return positive;
}
public CustomDialog setPositive(String positive, IOnPositiveListener positiveListener) {
this.positive = positive;
this.positiveListener = positiveListener;
return this;
}
public String getNegative() {
return negative;
}
public CustomDialog setNegative(String negative, IOnNegativeListener negativeListener) {
this.negative = negative;
this.negativeListener = negativeListener;
return this;
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.tv_positive:
positiveListener.positive(this);
break;
case R.id.tv_negative:
negativeListener.negative(this);
break;
}
}
public interface IOnPositiveListener {
void positive(CustomDialog customDialog);
}
public interface IOnNegativeListener {
void negative(CustomDialog customDialog);
}
}
布局文件的代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@drawable/layout_dialog">
<TextView
android:id="@+id/tv_tittle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="提示"
android:textSize="20sp"
android:gravity="center"
android:textColor="@color/colorGray"
android:layout_margin="20dp"/>
<TextView
android:id="@+id/tv_massage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="确定删除?"
android:textSize="20sp"
android:gravity="center"
android:textColor="@color/colorBlack"
android:layout_margin="20dp"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/colorLightGray"
android:layout_marginTop="20dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_positive"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="确定"
android:textSize="20sp"
android:textColor="@color/colorLightGrayishBlue"
android:gravity="center"
android:layout_weight="1"/>
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="@color/colorLightGray"/>
<TextView
android:id="@+id/tv_negative"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="取消"
android:textSize="20dp"
android:textColor="@color/colorLightGrayishBlue"
android:gravity="center"
android:layout_weight="1"/>
</LinearLayout>
</LinearLayout>
使用自定义类 CustomDialog
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_custom_dialog);
mBtnCustomDialog1 = findViewById(R.id.btn_custom_dialog_1);
mBtnCustomDialog1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
CustomDialog customDialog = new CustomDialog(CustomDialogActivity.this);
customDialog.setTittle("提示").setMassage("你确定删除吗?").setPositive("确定", new CustomDialog.IOnPositiveListener() {
@Override
public void positive(CustomDialog customDialog) {
}
}).setNegative("取消", new CustomDialog.IOnNegativeListener() {
@Override
public void negative(CustomDialog customDialog) {
}
}).show();
}
});
}
2.3 PopupWindow
它是一个容器, 可以把View放到里面去, 通过设计监听, 可以点弹出View
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_popup_window);
mBtnPop = findViewById(R.id.btn_pop);
mBtnPop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
View view = getLayoutInflater().inflate(R.layout.layout_pop, null);
mPop = new PopupWindow(view, mBtnPop.getWidth(), ViewGroup.LayoutParams.WRAP_CONTENT);
mPop.setOutsideTouchable(true);
mPop.setFocusable(true);
// mPop.setAnimationStyle();
mPop.showAsDropDown(mBtnPop);
final TextView textView = view.findViewById(R.id.tv_1);
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(PopupWindowActivity.this, textView.getText(), Toast.LENGTH_SHORT).show();
mPop.dismiss();
//do something
}
});
}
});
}
布局文件
// R.layout.layout_pop 布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_margin="5dp">
<TextView
android:id="@+id/tv_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="苹果"
android:gravity="center"
android:padding="5dp"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/colorGray"/>
<TextView
android:id="@+id/tv_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="雪梨"
android:gravity="center"
android:padding="5dp"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/colorGray"/>
<TextView
android:id="@+id/tv_3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="香蕉"
android:gravity="center"
android:padding="5dp"/>
</LinearLayout>
2月11号
1. 学习目标
- Activity
- Fragmen
2. 学习笔记
2.1 Activity
2.1.1 何为 Activity?
Activity主要是用来做控制的,它可以选择要显示的View,也可以从View中获取数据然后把数据传给Model层进行处理,最后再来显示出处理结果。
2.1.2 新建 Avtivity 分三步
- 创建类继承于 Activity 类或其子类, 如:
public class UIActivity extends AppCompatActivity { ...
- 在 AndroidManifest 文件中声明, 如:
<activity android:name=".UIActivity" />
- 创建布局文件, 并在重写的 onCreate() 方法中设置, 如:
setContentView(R.layout.activity_ui);
(通常情况下得需要这一步, 但并非必须的)
2.1.3 在 AndroidManifest 中设置 Avtivity 的相关属性
属性 | 作用 |
---|---|
label | 设置最顶端一栏, 也就是action bar 的文本内容 |
theme | 设置主题, 可以在 application 标签内设置, 这样就会运用到全部的activity中去 |
screenOrientation | 方向, 竖屏或者横屏, 或者全屏 |
intent-filter | 这个标签可以设置启动项 |
2.1.4 Activity 的生命周期
打印日志, 查看生命周期如何运行的
public class LifeCycleActivity extends AppCompatActivity {
private String eTag = "LifeCycle";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_life_cycle);
Log.e(eTag, "onCreate");
}
@Override
protected void onStart() {
super.onStart();
Log.e(eTag, "onStart");
}
@Override
protected void onResume() {
super.onResume();
Log.e(eTag, "onResume");
}
@Override
protected void onPause() {
super.onPause();
Log.e(eTag, "onPause");
}
@Override
protected void onStop() {
super.onStop();
Log.e(eTag, "onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e(eTag, "onDestroy");
}
}
2.1.5 Activity 的跳转
一般用显式1就好了, 其他的要看得懂
- 显式跳转 Activity , 四种方式:2
// 方式一
// 第一个参数: 当前Activity. 第二个参数: 目标 Activity
Intent intent = new Intent(JumpActivity.this, TargetActivity.class);
startActivity(intent);
// 方式二
Intent intent = new Intent();
intent.setClass(JumpActivity.this, TargetActivity.class);
startActivity(intent);
// 方式三
Intent intent = new Intent();
intent.setClassName(JumpActivity.this, "com.example.learningandroid.activity.jump.TargetActivity");
startActivity(intent);
// 方式四
Intent intent = new Intent();
intent.setComponent(new ComponentName(JumpActivity.this, "com.example.learningandroid.activity.jump.TargetActivity"));
startActivity(intent);
- 隐式跳转 Activity
需要在 AndroidManifest 文件的目标 Activity内增加一个标签, 具体如下:
// 第一个字符串随便起, 对应好就行, 第二个字符串得是DEFAULT
<intent-filter>
<action android:name="android.intent.action.jumpto" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
然后就可以使用显式跳转了
Intent intent = new Intent();
intent.setAction("android.intent.action.jumpto");
startActivity(intent);
2.1.6 Activity 的数据传递
- put: 使用键值对Bundle, 把要传的值 put 进去, 然后调用 intent 的 putExtras() 方法将 Bundle 传过去
Intent intent = new Intent(JumpActivity.this, TargetActivity.class);
Bundle bundle = new Bundle();
bundle.putString("name", "阿笑");
bundle.putInt("old", 18);
intent.putExtras(bundle);
- get, 调用 getIntent() 方法获取 Intent对象, 再调用 getExtras(); 方法获取传过来的 Bundle 对象, 再调用get方法就可以获取传过来的数据了
Bundle bundle = getIntent().getExtras();
mTv.setText(bundle.getString("name") + bundle.getInt("old"));
- startActivityForResult() 和 setResult
为了满足这样一个求: 跳转到另外一个 Activity , 处理完数据后回到原 Activity , 同时返回数据. 这就需要上述的两个方法
// 当前Activity中:
// 跳转 *Activity* 要用这个方法了
startActivityForResult(intent, 0);
// 同时要重写onActivityResult()方法以获取传回来的数据, 数据在data种
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Toast.makeText(JumpActivity.this, data.getExtras().getString("content"), Toast.LENGTH_LONG).show();
}
// 目标Activity中:
// 设置该按钮, 点击后回到原来的 *Activity*, 并调用 *setResult()* 方法传回数据, 和 *finish()* 方法关闭当前的 *Activity*
mBtnBack.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
Bundle bundle1 = new Bundle();
bundle1.putString("content", "I'm back!");
intent.putExtras(bundle1);
setResult(Activity.RESULT_OK, intent);
finish();
}
});
2.1.7 Activity 的4种启动模式
Android 的 Activity 是由栈管理的, 每启动一个 Activity 就会被放进栈种, 按返回键, 就会从栈顶移除一个 Activity .
在 AndroidManifest 文件种可以设置启动模式: android:launchMode
, 可以设置任务栈名称: android:taskAffinity
, 这相当于一个新的任务栈了, 这之后的都往这里放了.
有四种启动模式:可以参考 图解4种启动模式
- standar : 标准模式, 默认. 每启动一个 Activity 就会创建一个新的实例 (一直往上堆)
- singleTop : Task栈顶复用模式. 当要启动的 Activity 位于栈顶, 则不会创建实例, 而是复用这个栈顶的 Activity , 并且它的 onNewIntent() 方法会被调用. 如果目标 Activity 不在栈顶, 则跟标准模式一样 (最上面的重复就复用它)
- singleTask : Task栈内复用模式. 当要启动的 Activity 位于栈内, 则不会创建实例, 而是复用这个栈顶的 Activity , 并且它的 onNewIntent() 方法会被调用. 同时位于该 Activity 上方的全部被清除. 如果目标 Activity 不在栈内, 则跟标准模式一样 (里面重复就复用它, 并且通过清除使它成为最上面的)
- singleIstance : 全局单例模式. 一个任务栈里只有一个 Activity , 并且全局复用
2.2 Fragment 碎片
2.2.1 何为 Fragment
碎片(Fragment)是一种可以嵌入在活动当中的UI片段,它能让程序更加合理和充分地利用大屏幕的空间,因而在平板上应用的非常广泛。虽然碎片对你来说应该是个全新的概念,但我相信你学习起来应该毫不费力,因为它和活动实在是太像了,同样都能包含布局,同样都有自己的生命周期。你甚至可以将碎片理解成一个迷你型的活动,虽然这个迷你型的活动有可能和普通的活动是一样大的。
emmm…觉得会用不上, 就先不学了
二月12日
1.学习目标
- 书写4种事件监听的实现方式
- 理解基于回调的事件处理机制
- Handler消息处理
2. 学习笔记
2.1 4种事件监听的实现方式
要点: 监听器的创建由许多种方式, 可以直接匿名内部类, 也可以写个类, 再实例化它来使用, 还可以让事件源所在的类实现监听接口, 然后直接用本类来创建监听器, 更是可以通过外部自定义监听器创建的类, 然后实例它来实现, 所以就由以下4种方法来设置事件监听
public class EventActivity extends AppCompatActivity implements View.OnClickListener {
private Button mBtnToast;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event);
mBtnToast = findViewById(R.id.btn_toast);
// 方式一: 内部类
// mBtnToast.setOnClickListener(new OnClick());
// 方式二: 匿名内部类
// mBtnToast.setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View v) {
// Toast.makeText(EventActivity.this, "匿名内部类实现事件监听", Toast.LENGTH_SHORT).show();
// }
// });
// 方式三: 通过事件源所在的类来实现事件监听
// mBtnToast.setOnClickListener(EventActivity.this);
// 方式四: 通过外部类来实现
mBtnToast.setOnClickListener(new MyListener(this));
}
@Override
public void onClick(View v) {
Toast.makeText(EventActivity.this, "通过事件源所在的类来实现事件监听", Toast.LENGTH_SHORT).show();
}
private class OnClick implements View.OnClickListener {
@Override
public void onClick(View v) {
Toast.makeText(EventActivity.this, "内部类实现事件监听", Toast.LENGTH_SHORT).show();
}
}
}
public class MyListener implements View.OnClickListener {
private Activity activity;
public MyListener(Activity activity) {
this.activity = activity;
}
@Override
public void onClick(View v) {
Toast.makeText(this.activity, "通过外部类来实现", Toast.LENGTH_SHORT).show();
}
}
当然, 还有第五种方式, 也就是通过布局文件设置 android:onClick=“方法名” ,并在类中实现这个方法, 注意访问限制符是 public , 参数是也可以实现
android:onClick="show"
public void show(View v) {
Toast.makeText(EventActivity.this, "通过布局文件来实现", Toast.LENGTH_SHORT).show();
}
注意 : 监听器最终起作用的是最后建立的.
2.2 基于回调的事件处理机制
- 事件的处理是由内向外依次传播的, 只有监听返回值是 true, 事件处理才会终止, 不然会一直往外传播.
- 如果由设立了事件监听器, 那么就从监听器开始, 然后再由内往外传
- 所谓的 由内往外传 是指 : 比如一个Button控件的触发事件的处理, 就需要从最里边的触发方法开始调用, 依次往外.
2.3 Handler消息处理
利用 Handler 实现需求 : 在某一 Activity 停留3秒后跳转到另外一个 Activity
mHandler = new Handler();
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
Intent intent = new Intent();
intent.setClass(HandlerActivity.this, ButtonActivity.class);
startActivity(intent);
}
}, 3000);
Handler 可以实现上述功能, 但一般不拿来这么用, 一般会结合新建的线程, 进行线程之间的通信:
mHandler = new Handler() { // 主线程
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what) { // 根据不同的消息进行不一样的操作
case 1 :
Toast.makeText(HandlerActivity.this, "线程通讯成功", Toast.LENGTH_SHORT).show();
break;
default :
}
}
};
// 新建一个线程
new Thread(){
@Override
public void run() {
super.run();
Message message = new Message();
message.what = 1; // 消息识别标识
mHandler.sendMessage(message); // Handler发送消息
}
}.start();
二月13日
1. 学习目标
- SharePreference轻量数据处理
- Android 存储概念
- File 内部存储
- File 外部存储
- BroadcastReceiver
2. 学习笔记
2.1 SharePreference轻量数据处理
- 数据读取用 : SharePreference
- 数据存储用 : SharePreference.Editor
- 存储数据 :
mEditor.putString("content", mEtInputContent.getText().toString()); mEditor.apply();
- 读取数据 :
mSharedPreferences.getString("content", "no content")
例子:
public class SharedPreferencesActivity extends AppCompatActivity {
private EditText mEtInputContent;
private Button mBtnSave, mBtnShow;
private TextView mTvShow;
private SharedPreferences mSharedPreferences; // 用来读轻量的数据
private SharedPreferences.Editor mEditor; // 用来写轻量的数据
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_shared_preferences);
mEtInputContent = findViewById(R.id.et_input_content);
mBtnSave = findViewById(R.id.btn_save_data);
mBtnShow = findViewById(R.id.btn_show_data);
mTvShow = findViewById(R.id.tv_show_content);
mSharedPreferences = getSharedPreferences("data", MODE_PRIVATE);
mEditor = mSharedPreferences.edit();
mBtnSave.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// putString(), 键值对的形式存数据
mEditor.putString("content", mEtInputContent.getText().toString());
mEditor.apply();
}
});
mBtnShow.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// getString(), 取数据
mTvShow.setText(mSharedPreferences.getString("content", "no content"));
}
});
}
}
2.2 Android 存储概念
参考资料 : 五大数据存储
2.3 File内部存储
- 数据存储分三步
- 打开输出流 :
fileOutputStream = openFileOutput(mFILENAME, MODE_PRIVATE)
- 写入数据 :
fileOutputStream.write(content.getBytes())
- 关闭输出流 :
fileOutputStream.close();
- 打开输出流 :
- 读取数据分三步
- 打开输入流 :
fileInputStream = openFileInput(mFILENAME);
- 读取数据 :
fileInputStream.read(buff)
- 关闭输出流 :
fileInputStream.close();
- 打开输入流 :
- 综合代码:
public class FileActivity extends AppCompatActivity {
private EditText mEtInputContent;
private Button mBtnSave, mBtnShow;
private TextView mTvShow;
private final String mFILENAME = "test.txt";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_file);
mEtInputContent = findViewById(R.id.et_input_content);
mBtnSave = findViewById(R.id.btn_save_data);
mBtnShow = findViewById(R.id.btn_show_data);
mTvShow = findViewById(R.id.tv_show_content);
mBtnSave.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
save(mEtInputContent.getText().toString());
}
});
mBtnShow.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mTvShow.setText(read());
}
});
}
// 存储数据
private void save(String content) {
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = openFileOutput(mFILENAME, MODE_PRIVATE);
fileOutputStream.write(content.getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 读取数据
private String read() {
FileInputStream fileInputStream = null;
try {
fileInputStream = openFileInput(mFILENAME);
byte[] buff = new byte[1024];
StringBuilder stringBuilder = new StringBuilder(""); // 一个可变长字符串
int len = 0;
while ((len = fileInputStream.read(buff)) > 0) {
stringBuilder.append(new String(buff, 0, len));
}
return stringBuilder.toString();
}catch (IOException e) {
e.printStackTrace();
} finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
}
2.4 File外部存储
外部存储的访问需要设置权限以及动态申请权限, 首先得再 AndroidManifest 文件中申请权限 : <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
, 然后得动态申请用户的权限:
String[] permissions = new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE};
List<String> mUnPermissionList = new ArrayList<>();
// private ImageView welcomeImg = null;
private static final int PERMISSION_REQUEST = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
checkPermission();
setContentView(R.layout.activity_main);
}
// 检查权限
private void checkPermission() {
mUnPermissionList.clear();
//判断哪些权限未授予
for (int i = 0; i < permissions.length; i++) {
if (ContextCompat.checkSelfPermission(this, permissions[i]) != PackageManager.PERMISSION_GRANTED) {
mUnPermissionList.add(permissions[i]);
}
}
/**
* 判断是否为空
*/
if (mUnPermissionList.isEmpty()) {//未授予的权限为空,表示都授予了
} else {//请求权限方法
String[] permissions = mUnPermissionList.toArray(new String[mUnPermissionList.size()]);//将List转为数组
ActivityCompat.requestPermissions(MainActivity.this, permissions, PERMISSION_REQUEST);
}
}
/**
* 响应授权
* 这里不管用户是否拒绝,都进入首页,不再重复申请权限
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case PERMISSION_REQUEST:
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
break;
}
}
其余部分与内部存储相比, 就只是多了一步, 即最先得创建文件.
- 数据存储分四步
- 创建文件类对象 :
// 创建文件夹对象 File dir = new File(Environment.getExternalStorageDirectory(), "axiao"); if (!dir.exists()) { // 如果该文件夹不存在的话就创建目标文件夹 dir.mkdirs(); } // 创建文件对象 File file = new File(dir, mFILENAME); if (!file.exists()) { // 如果该文件不存在的话就目标创建文件 file.createNewFile(); }
- 创建输出流对象 :
fileOutputStream = new FileOutputStream(file); // 创建一个通向目标文件的输出流
- 写文件 :
fileOutputStream.write(content.getBytes());
- 关闭输出流对象 :
fileOutputStream.close();
- 数据读取分四步
- 创建文件类对象 :
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "axiao", mFILENAME);
- 创建输入流对象 :
fileInputStream = new FileInputStream(file);
- 读取数据 :
fileInputStream.read(buff)
- 关闭输入流对象 :
fileInputStream.close();
- 创建文件类对象 :
- 综合代码
public class FileActivity extends AppCompatActivity {
private EditText mEtInput;
private Button mBtnSave, mBtnShow;
private TextView mTvShow;
private final String mFILENAME = "test.txt";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_file);
mEtInput = findViewById(R.id.et_input);
mBtnSave = findViewById(R.id.btn_save_data);
mBtnShow = findViewById(R.id.btn_show_data);
mTvShow = findViewById(R.id.tv_show_content);
mBtnSave.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
save(mEtInput.getText().toString());
}
});
mBtnShow.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mTvShow.setText(read());
}
});
}
// 存储数据
private void save(String content) {
FileOutputStream fileOutputStream = null;
try {
// 内部存储
// fileOutputStream = openFileOutput(mFILENAME, MODE_PRIVATE);
// 外部存储
// 创建文件夹对象
File dir = new File(Environment.getExternalStorageDirectory(), "axiao");
if (!dir.exists()) { // 如果该文件夹不存在的话就创建目标文件夹
dir.mkdirs();
}
// 创建文件对象
File file = new File(dir, mFILENAME);
if (!file.exists()) { // 如果该文件不存在的话就目标创建文件
file.createNewFile();
}
fileOutputStream = new FileOutputStream(file); // 创建一个通向目标文件的输出流
fileOutputStream.write(content.getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2.5 BroadcastReceiver
一般不常用…要用再说, 自己写代码时出现一个问题, 广播成功发送了, 并且广播也成功注册了, 但是收不到广播, 最后查明原因是: 我发送广播的时刻, 接收广播的Activity的 onDestroy() 方法已经调用了, 也就是已经销毁了, 故我再进去的时候还是没有之前的数据, 即没有接收到广播. 于是吸取教训: 运用广播的时候, 一般是之前的 Activity 发送广播, 而在它之后的 Activity 来接受广播, 这样才能保证发送广播的时刻, 接收广播的 Activity 也存在.
广播的使用很简单, 就分为四个部分:
- 发送广播 :
LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.test.action"));
- 注册广播 :
LocalBroadcastManager.getInstance(context).registerReceiver(deviceEventReceiver, intentFilter);
- 注销广播 : 为了防止内存泄漏, 这一步骤一般放在接收 Activity 的onDestroy() 方法中 :
LocalBroadcastManager.getInstance(context).unregisterReceiver(deviceEventReceiver)
以下代码参考自 火龙映天
Intent intent = new Intent("com.test.action");
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
接收广播:
//注册广播
private void registerDeviceEventReceiver() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.test.action");
LocalBroadcastManager.getInstance(context).registerReceiver(deviceEventReceiver, intentFilter);
}
//注销广播
private void unregisterDeviceEventReceiver() {
LocalBroadcastManager.getInstance(context).unregisterReceiver(deviceEventReceiver);
}
//广播接收器
private BroadcastReceiver deviceEventReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() == null) {
return;
}
if ("com.test.action".equals(intent.getAction())) {
//TODO: 逻辑处理
}
}
};