安卓开发日记——MyDiary(1)

一直有想写博客的想法,无奈自己太懒。现在疫情期间,不开学,上网课,日子过得更加浑浑噩噩,下定决心开发一个记录学习生活的小项目,加以博客的形式记录学习历程,帮助自己提升水准。

废话说完,大概看一下项目目前的样子:

示例
设计了一个比较普通的功能:

  • 在计划页面设置每日的计划,并在主页显示,同时在主页可以点击计划将其划掉,代表今日已完成该计划。

要实现这个功能,我认为几个比较重要的步骤是:

  • 设计数据库及数据表
  • 自定义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的长度,就删除数据库中多余的数据。
    之所以要使用深度拷贝,是因为如果使用浅拷贝的话,两个列表仍指向同一个地址,即改变一个列表中内容的同时也会改变另一个列表,这样达不到需要的效果

参考博客(深度拷贝)
https://www.iteye.com/blog/bijian1013-2358367

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>
本项目是一个基于安卓的日记本项目源码,本站之前介绍过很多关于日记本/备忘录/便签这一类型的项目源码。进入应用首先进入欢迎界面会有一个开门效果,点击”进入日记”即可进入主界面,在主界面可以点击进入”写日记”、”查看日记”、”搜索日记”、”日记加密”、”退出”以及右下角path菜单按钮查看更多。进入”写日记”界面即可写日记并且可以选择当天天气情况,写完日记以后不需要其他操作直接点返回键就可以自动保存内容并回到主界面。进入”查看日记”界面即可查看写过的日记,若没有写过日记,则提示用户写日记。进入”搜索日记”界面即可对日记内容进行搜索,搜索日记功能可以根据关键字模糊搜索并且可以即时出现结果。进入”日记加密”界面即可对日记进行加密,密码保护部分可以设置日志的数字密码或者图形密码,设置完成退出应用以后再次打开应用就会出现要求输入数字密码或者绘制图像密码的界面。点击右下方按钮会弹出弧形菜单,可进入相应操作。如关于、帮助、夜间模式、换背景、设置提醒、意见反馈。”换背景”操作,手动换屏,长按图片或者按菜单键按提示操作即可。”设置提醒”可以设置提醒写日记的时间。不得不说在本项目的开发过程中作者考虑的情况很周全,对用户体验方面也下了很大功夫。例如无需手动保存、可以选择天气、带有密码设置、即时搜索出结果等等功能都可以给使用者提供不错的用户体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值