一直有想写博客的想法,无奈自己太懒。现在疫情期间,不开学,上网课,日子过得更加浑浑噩噩,下定决心开发一个记录学习生活的小项目,加以博客的形式记录学习历程,帮助自己提升水准。
废话说完,大概看一下项目目前的样子:
设计了一个比较普通的功能:
- 在计划页面设置每日的计划,并在主页显示,同时在主页可以点击计划将其划掉,代表今日已完成该计划。
要实现这个功能,我认为几个比较重要的步骤是:
- 设计数据库及数据表
- 自定义Dialog以显示修改计划的对话框
- 自定义Adapter类在RecyclerView上渲染数据
总结
1.数据库方面:
我使用的是DBFlow来存储和操作数据。
官方目前的版本是5.0.0-alpha1,github地址:https://github.com/agrosner/DBFlow
由于个人水平原因,使用时出现一些暂时无法解决的异常,随即在网上查阅发现DBFlow(4.2.4)版本的使用方法,故在此使用的是4.2.4版本。
参考博客:
https://www.cnblogs.com/xxdh/p/9282504.html
https://www.cnblogs.com/ChenJanson/p/5363420.html
配置DBFlow:
在项目目录的build.gradle中加入:
allprojects {
repositories {
...
maven { url "https://jitpack.io" }
}
}
在工程目录的build.gradle中加入:
def dbflow_version = "4.2.4"
dependencies {
...
annotationProcessor "com.github.Raizlabs.DBFlow:dbflow-processor:$dbflow_version"
implementation "com.github.Raizlabs.DBFlow:dbflow-core:$dbflow_version"
implementation "com.github.Raizlabs.DBFlow:dbflow:$dbflow_version"
}
创建数据库:
@Database(name = MyDatabase.NAME,version = MyDatabase.VERSION)
public class MyDatabase{
//数据库名称
public static final String NAME = "MyDatabase";
//版本号,因为更新过表的字段所以版本号在此为"3"
public static final int VERSION = 3;
//更新数据表时需要的操作,具体可以参考我列出的博客
@Migration(version = 3,database = MyDatabase.class)
public static class Migration_3_Plan extends AlterTableMigration<Plan>{
public Migration_3_Plan(Class<Plan> table) {
super(table);
}
//为表<Plan>添加了一个名为"isFinished"的字段
@Override
public void onPreMigrate() {
addColumn(SQLiteType.get(boolean.class.getName()),"isFinished");
}
}
}
创建Model
@Table(database = MyDatabase.class)
//继承BaseModel,BaseModel包含了基本的数据库操作
public class Plan extends BaseModel implements Serializable {
@PrimaryKey
public int id;
//计划的内容
@Column
public String content;
//计划的日期
@Column
public Date date;
//该计划是否是日常的
@Column
public Boolean isDaily;
//新增的字段,记录计划是否已经完成,默认值为false
@Column
public Boolean isFinished = false;
}
初始化DBFlow
public class MyApplication extends Application {
private static Context mContext;
@Override
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
//初始化DBflow
FlowManager.init(this);
}
public static Context getContext(){
return mContext;
}
}
之后就可以进行数据库的操作了。
2.自定义对话框(包含自定义适配器):
当点击计划页面的“修改”按钮时弹出,用于修改计划内容。以下是实现时需注意的点:
- 获取所有想要自定义的组件,并通过setXX方法以在外部自定义
- 对话框中列表数据发生变化时,需调用适配器(adapter)的通知(notify)方法通知相应的数据发生了变化,嫌麻烦可直接使用adapter.notifyDataSetChanged()方法,只是这样做不会有删除和添加的动画效果以及更耗资源,虽然这里数据量比较少不会有太大损耗就是了
参考博客
https://blog.csdn.net/qq_33919497/article/details/79877430?utm_source=blogxgwz3
public class PlanDialog extends Dialog {
private LinearLayout container; //布局容器
private Button confirmBtn,cancelBtn; //确定和取消按钮
private RecyclerView planRv; //计划列表
private TextView addTv; //添加计划的文本按钮
private TextView titleTv;
private int backgroundId; //自定义背景图片的资源ID
private String confirmText,cancelText,addText; //对应按钮显示的文本
private String titleText; //对话框的标题文本
private RecyclerView.Adapter adapter; //计划列表的适配器
private onConfirmBtnClickListener onConfirmBtnClickListener; //监听确认按钮的点击事件
private onCancelBtnClickListener onCancelBtnClickListener; //监听取消按钮的点击事件
private onModifyTextClickListener onModifyTextClickListener; //监听修改按钮的点击事件
private Context mContext;
public PlanDialog(@NonNull Context context) {
super(context);
mContext = context;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.dialog_plan);
//设置点击空白处是否关闭对话框
setCanceledOnTouchOutside(false);
//初始化界面组件
initView();
//初始化数据
initData();
//初始化按钮的点击事件
initEvent();
}
/**
* 设置对话框背景图片
* @param resourceId 自定义背景图片资源ID
*/
public void setBackground(int resourceId){
backgroundId = resourceId;
}
/**
* 设置对话框标题
* @param title 自定义标题内容
*/
public void setTitle(String title){
titleText = title;
}
/**
* 设置列表的适配器
*/
public void setAdapter(RecyclerView.Adapter adapter){
this.adapter = adapter;
}
/**
* 设置确认按钮的显示内容与监听
* @param text 自定义按钮文本
* @param onConfirmBtnClickListener 自定义监听器
*/
public void setOnConfirmBtnClickListener(String text,onConfirmBtnClickListener onConfirmBtnClickListener){
if(text != null){
confirmText = text;
}
this.onConfirmBtnClickListener = onConfirmBtnClickListener;
}
/**
* 设置取消按钮的显示内容与监听
* @param text 自定义按钮文本
* @param onCancelBtnClickListener 自定义监听器
*/
public void setOnCancelBtnClickListener(String text,onCancelBtnClickListener onCancelBtnClickListener){
if(text != null){
cancelText = text;
}
this.onCancelBtnClickListener = onCancelBtnClickListener;
}
/**
* 设置修改按钮的监听
* @param onModifyTextClickListener 自定义监听器
*/
public void setOnModifyTextClickListener(String text,onModifyTextClickListener onModifyTextClickListener){
if(text != null){
addText = text;
}
this.onModifyTextClickListener = onModifyTextClickListener;
}
/**
* 初始化界面组件
*/
private void initView(){
container = findViewById(R.id.plan_container);
confirmBtn = findViewById(R.id.confirm_btn);
cancelBtn = findViewById(R.id.cancel_btn);
planRv = findViewById(R.id.plan_rv);
addTv = findViewById(R.id.plan_add_tv);
titleTv = findViewById(R.id.plan_title_tv);
LinearLayoutManager layoutManager = new LinearLayoutManager(mContext);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
planRv.setLayoutManager(layoutManager);
}
/**
* 初始化数据
*/
private void initData(){
if(backgroundId != 0){
container.setBackgroundResource(backgroundId);
}
if(titleText != null){
titleTv.setText(titleText);
}
if(confirmText != null){
confirmBtn.setText(confirmText);
}
if(cancelText != null){
cancelBtn.setText(cancelText);
}
if(addText != null){
addTv.setText(addText);
}
if(adapter != null){
planRv.setAdapter(adapter);
}
}
/**
* 初始化按钮的点击事件
*/
private void initEvent(){
confirmBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(onConfirmBtnClickListener != null){
onConfirmBtnClickListener.onConfirmBtnClick();;
}
}
});
cancelBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(onCancelBtnClickListener != null){
onCancelBtnClickListener.onCancelBtnClick();
}
}
});
addTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(onModifyTextClickListener != null){
onModifyTextClickListener.onModifyTextClick();
}
}
});
}
/**
* 设置按钮被点击的接口
*/
public interface onConfirmBtnClickListener{
void onConfirmBtnClick();
}
public interface onCancelBtnClickListener{
void onCancelBtnClick();
}
public interface onModifyTextClickListener{
void onModifyTextClick();
}
/**
* 显示对话框
*/
@Override
public void show() {
super.show();
//设置对话框的大小,要设置在show()方法之后
WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
layoutParams.height = ScreenUtils.getScreenHeight(mContext) * 3/5;
layoutParams.width = ScreenUtils.getScreenWidth(mContext) * 5/6;
getWindow().setAttributes(layoutParams);
}
/**
* 通知列表更新对应位置的数据
*/
public void notifyInserted(int position){
if (adapter != null) {
adapter.notifyItemInserted(position);
}
}
public void notifyChanged(int position){
if (adapter != null) {
adapter.notifyItemChanged(position);
}
}
public void notifyRemoved(int position){
if (adapter != null) {
adapter.notifyItemRemoved(position);
}
}
public void updateAll(){
if (adapter != null) {
adapter.notifyDataSetChanged();
}
}
}
布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="@+id/plan_container"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="8dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/plan_title_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:textColor="@color/black"
android:text="计划"/>
<TextView
android:id="@+id/plan_add_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:textColor="@color/red"
android:text="添加"/>
</RelativeLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/plan_rv"
android:layout_width="match_parent"
android:layout_height="240dp">
</android.support.v7.widget.RecyclerView>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/cancel_btn"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true"
android:background="@drawable/selector_btn_plan"
android:text="取消"/>
<Button
android:id="@+id/confirm_btn"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:background="@drawable/selector_btn_plan"
android:text="保存"/>
</RelativeLayout>
</LinearLayout>
使用自定义对话框(部分代码省略),此处可先无视自定义适配器的内容(注释内容)
private void showDialog(){
//实例化自定义对话框
final PlanDialog dailyPlanDialog = new PlanDialog((getActivity());
//设置对话框标题
dailyPlanDialog.setTitle("每日计划");
//设置背景图片
dailyPlanDialog.setBackground(R.drawable.pic_bg_dialog);
// //设置列表适配器
// final DailyPlanSettingAdapter settingAdapter = new DailyPlanSettingAdapter(getActivity(),R.layout.item_plan_setting,temporaryList);
//
// /* 设置自定义监听器 */
// //计划文本更新的监听器
// settingAdapter.setOnTextChangedListener(new DailyPlanSettingAdapter.onTextChangedListener() {
// @Override
// public void onTextChanged(int position, Editable text) {
// Plan plan = temporaryList.get(position);
// plan.content = text.toString();
// temporaryList.set(position,plan);
// }
// });
//
// //删除按钮点击事件监听器
// settingAdapter.setOnDeleteBtnClickListener(new DailyPlanSettingAdapter.onDeleteBtnClickListener() {
// @Override
// public void onDeleteBtnClick(int position) {
// temporaryList.remove(position);
// //通知Dialog中的适配器更新内容
// dailyPlanDialog.notifyRemoved(position);
// while (position < temporaryList.size()){
// dailyPlanDialog.notifyChanged(position);
// position++;
// }
// }
// });
// dailyPlanDialog.setAdapter(settingAdapter);
//设置按钮的文本及监听事件
//保存按钮
dailyPlanDialog.setOnConfirmBtnClickListener("保存",new PlanDialog.onConfirmBtnClickListener() {
@Override
public void onConfirmBtnClick() {
//执行需要的操作
...
//关闭对话框
dailyPlanDialog.dismiss();
}
});
//取消按钮
dailyPlanDialog.setOnCancelBtnClickListener("取消", new PlanDialog.onCancelBtnClickListener() {
@Override
public void onCancelBtnClick() {
//关闭对话框
dailyPlanDialog.dismiss();
}
});
//添加按钮
dailyPlanDialog.setOnModifyTextClickListener("添加",new PlanDialog.onModifyTextClickListener() {
@Override
public void onModifyTextClick() {
//执行需要的操作
...
}
});
//显示对话框
dailyPlanDialog.show();
}
自定义适配器(设置计划)
public class DailyPlanSettingAdapter extends RecyclerView.Adapter<DailyPlanSettingAdapter.ViewHolder>{
private List<Plan> mPlanList; //需要渲染的列表数据
private int mLayout; //需要渲染的列表布局id
private Context mContext;
private onDeleteBtnClickListener onDeleteBtnClickListener; //监听删除按钮的点击事件
private onTextChangedListener onTextChangedListener; //监听文本更新事件
/**
* 构造方法
* @param context 应用上下文
* @param layout 布局资源id
* @param planList 列表数据
*/
public DailyPlanSettingAdapter(Context context, int layout, List<Plan> planList){
mContext = context;
mLayout = layout;
mPlanList = planList;
}
/**
* ViewHolder
*/
static class ViewHolder extends RecyclerView.ViewHolder {
EditText planEt;
Button deleteBtn;
ViewHolder(@NonNull View itemView) {
super(itemView);
planEt = itemView.findViewById(R.id.plan_edit);
deleteBtn = itemView.findViewById(R.id.delete_btn);
}
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(mContext).inflate(mLayout,viewGroup,false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull final ViewHolder viewHolder, int i) {
Plan plan = mPlanList.get(i);
final EditText mEt = viewHolder.planEt;
final Button mBtn = viewHolder.deleteBtn;
final int position = viewHolder.getAdapterPosition();
//设置文本
mEt.setText(plan.content);
//绑定文本更新的监听器
final Watcher mWatcher = new Watcher(position);
//仅当文本框获得焦点时才添加文本更新的监听器,失去焦点时删除该监听器
mEt.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean b) {
if(b) {
mEt.addTextChangedListener(mWatcher);
} else {
mEt.removeTextChangedListener(mWatcher);
}
}
});
//绑定删除按钮的点击事件的监听器
mBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//设置禁止再次点击,否则因某些原因连续点击程序将闪退
mBtn.setClickable(false);
//回调点击的按钮在列表中的位置
onDeleteBtnClickListener.onDeleteBtnClick(position);
}
});
}
@Override
public int getItemCount() {
return mPlanList.size();
}
/**
* 设置删除按钮监听器
* @param onDeleteBtnClickListener 自定义监听器
*/
public void setOnDeleteBtnClickListener(onDeleteBtnClickListener onDeleteBtnClickListener){
this.onDeleteBtnClickListener = onDeleteBtnClickListener;
}
/**
* 设置文本更新监听器
* @param onTextChangedListener 自定义监听器
*/
public void setOnTextChangedListener(onTextChangedListener onTextChangedListener){
this.onTextChangedListener = onTextChangedListener;
}
/**
* 删除按钮点击事件接口
*/
public interface onDeleteBtnClickListener{
void onDeleteBtnClick(int position);
}
/**
* 文本更新事件接口
*/
public interface onTextChangedListener{
void onTextChanged(int position,Editable text);
}
/**
* 自定义TextWatcher
*/
class Watcher implements TextWatcher {
private int position;
Watcher(int position){
this.position = position;
}
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
//在文本框内容更新后,回调对应文本框的位置和内容
onTextChangedListener.onTextChanged(position,editable);
}
};
}
布局非常简单,一个输入框和一个按钮
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp">
<EditText
android:id="@+id/plan_edit"
android:layout_width="220dp"
android:layout_height="28dp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:paddingStart="6dp"
android:paddingEnd="6dp"
android:layout_alignParentStart="true"
android:background="@drawable/selector_edit_plan"
android:textSize="16sp"
android:maxLength="12"
android:hint="制定一个计划吧"/>
<Button
android:id="@+id/delete_btn"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_alignParentEnd="true"
android:background="@drawable/bg_btn_delete"
android:textSize="20sp"
android:text="×"
android:textColor="@color/white"/>
</RelativeLayout>
3.完整的计划页面
在计划页面中定义并使用对话框,编写数据库操作的代码,得到完整的页面。此处需要注意的是:
- 之前说过的列表数据变化后,适配器通知更新的问题
- 这里仅展示了DailyPlanSettingAdapter,即设置日常计划用的适配器,另外使用的DailyPlanDisplayAdapter未展示,但实际相差不大,甚至更简单
- 在此使用了一种比较笨的方法更新数据,当点击修改按钮时,深度拷贝一份当前的计划列表到临时列表中,即这段代码
temporaryList = Utils.deepCopy(dailyPlanList);
这样一来,dailyPlanList中保存了原有的计划数据,temporaryList中保存了变化后的计划数据。保存时,将temporaryList中的数据保存进数据库(控制主键的值以达到覆盖效果),如果发现temporaryList的长度小于dailyPlanList的长度,就删除数据库中多余的数据。
之所以要使用深度拷贝,是因为如果使用浅拷贝的话,两个列表仍指向同一个地址,即改变一个列表中内容的同时也会改变另一个列表,这样达不到需要的效果
public class PlanFragment extends Fragment implements View.OnClickListener{
private ImageView dailyPlanIcon; //装饰图标
private TextView dailyPlanTv; //修改日常计划的文本按钮
private RecyclerView dailyPlanRv; //显示每日计划的列表
private List<Plan> dailyPlanList = new ArrayList<>(); //保存Plan数据的列表
private List<Plan> temporaryList = new ArrayList<>(); //用于保存修改中的Plan,最终用于更新数据库
private DailyPlanDisplayAdapter displayAdapter; //展示每日计划的列表的适配器
private final static int MAX_DAILY_PLAN_NUM = 7; //每日计划数量最大值
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.fragment_plan,container,false);
/* 获取组件,绑定图片资源 */
dailyPlanIcon = view.findViewById(R.id.daily_plan_img);
dailyPlanTv = view.findViewById(R.id.daily_modify_tv);
dailyPlanRv = view.findViewById(R.id.daily_plan_rv);
dailyPlanIcon.setImageResource(R.drawable.ic_plan1);
/* 从数据库获取Plan数据 */
updatePlanList();
/* 显示DailyPlan列表 */
displayAdapter = new DailyPlanDisplayAdapter(getContext(),R.layout.item_plan_display,dailyPlanList,0);
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
dailyPlanRv.setLayoutManager(layoutManager);
dailyPlanRv.setAdapter(displayAdapter);
/* 绑定点击事件 */
dailyPlanTv.setOnClickListener(this);
return view;
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.daily_modify_tv:
//使用临时列表展示数据,并保存用户的修改后的数据(深度拷贝)
temporaryList.clear();
try {
temporaryList = Utils.deepCopy(dailyPlanList);
} catch (Exception e) {
e.printStackTrace();
}
//显示修改计划的对话框
showDialog();
break;
default:
break;
}
}
private void showDialog(){
//实例化自定义对话框
final PlanDialog dailyPlanDialog = new PlanDialog(Objects.requireNonNull(getActivity()));
//设置对话框标题
dailyPlanDialog.setTitle("每日计划");
//设置背景图片
dailyPlanDialog.setBackground(R.drawable.pic_bg_dialog);
//设置列表适配器
final DailyPlanSettingAdapter settingAdapter = new DailyPlanSettingAdapter(getActivity(),R.layout.item_plan_setting,temporaryList);
/* 设置自定义监听器 */
//计划文本更新的监听器
settingAdapter.setOnTextChangedListener(new DailyPlanSettingAdapter.onTextChangedListener() {
@Override
public void onTextChanged(int position, Editable text) {
//System.out.println("position is " + position + " text is " + text);
Plan plan = temporaryList.get(position);
plan.content = text.toString();
temporaryList.set(position,plan);
}
});
//删除按钮点击事件监听器
settingAdapter.setOnDeleteBtnClickListener(new DailyPlanSettingAdapter.onDeleteBtnClickListener() {
@Override
public void onDeleteBtnClick(int position) {
temporaryList.remove(position);
//通知Dialog中的适配器更新内容
dailyPlanDialog.notifyRemoved(position);
while (position < temporaryList.size()){
dailyPlanDialog.notifyChanged(position);
position++;
}
}
});
dailyPlanDialog.setAdapter(settingAdapter);
//设置按钮的文本及监听事件
//保存按钮
dailyPlanDialog.setOnConfirmBtnClickListener("保存",new PlanDialog.onConfirmBtnClickListener() {
@Override
public void onConfirmBtnClick() {
int position = 0;
for (Plan plan : temporaryList){
plan.isDaily = true;
plan.id = position;
plan.save();
position++;
System.out.println(plan);
}
//如果修改后列表的长度小于原列表,则删除数据库多余的数据
int x = dailyPlanList.size() - temporaryList.size();
while (x > 0)
{
dailyPlanList.get(dailyPlanList.size() - x).delete();
x--;
}
updatePlanList();
displayAdapter.notifyDataSetChanged();
dailyPlanDialog.dismiss();
}
});
//取消按钮
dailyPlanDialog.setOnCancelBtnClickListener("取消", new PlanDialog.onCancelBtnClickListener() {
@Override
public void onCancelBtnClick() {
dailyPlanDialog.dismiss();
}
});
//添加按钮
dailyPlanDialog.setOnModifyTextClickListener("添加",new PlanDialog.onModifyTextClickListener() {
@Override
public void onModifyTextClick() {
if(temporaryList.size() < MAX_DAILY_PLAN_NUM){
temporaryList.add(new Plan());
//通知Dialog中的适配器更新内容
dailyPlanDialog.notifyInserted(temporaryList.size() - 1);
} else {
Toast.makeText(getActivity(),"每日计划不宜过多哦~",Toast.LENGTH_SHORT).show();
}
}
});
dailyPlanDialog.show();
}
/**
* 更新Plan列表
*/
private void updatePlanList(){
dailyPlanList.clear();
dailyPlanList.addAll(
SQLite.select()
.from(Plan.class)
.where(Plan_Table.isDaily.is(true))
.queryList()
);
}
}
计划页面的布局文件
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
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:background="@color/whiteSmoke">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways"
android:background="@drawable/action_bar_bg">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:textStyle="bold"
android:textSize="18sp"
android:text="计划"/>
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp"
android:background="@color/white">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/daily_plan_img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@+id/daily_plan_img"
android:layout_centerVertical="true"
android:layout_marginStart="4dp"
android:text="每日计划"/>
<TextView
android:id="@+id/daily_modify_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:text="修改"/>
</RelativeLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/daily_plan_rv"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>