简易的便签应用开发

简易的便签应用开发

实现的功能

  • 主界面的便签新增功能:点击可以添加便签,增加新的标签页
  • 便签的修改功能:点击已有的便签可以进行进入便签的详情页,可以对便签进行内容的修改
  • 便签的长按批量操作功能:长按主界面的便签会在下方弹出编辑栏实现批量操作功能
    1. 实现便签的全选
    2. 实现便签的反选
    3. 实现便签的置顶,置顶的便签会有标记(没有取消置顶功能,考虑到便签的即时性)
    4. 实现便签的删除
  • 便签的时间显示功能:显示便签的最后修改时间,除去置顶便签外所有便签自动按照时间进行排序
  • 便签的数据保存功能:所有便签数据保存在本地,不会随应用退出而丢失
  • 其他的小功能:
    1. 加入了双击返回的退出机制,防止误触
    2. 加入了delete选项的提示,防止误删
    3. 加入了一些操作的消息提示
    4. 给编辑栏设置了进入和退出的动画

应用的具体实现

前期的准备工作

Item的属性封装

将ListView中每个item的属性全部封装在ItemBean中,方便管理

import java.io.Serializable;
import java.util.Calendar;
import java.util.Date;

public class ItemBean implements Serializable {
    private String title;
    private String content;

    private boolean isShow; // 是否显示CheckBox
    private boolean isChecked; // 是否选中CheckBox

    private int id = -1; //等价于item的position,初始值-1表示为新添加的便签

    private Date date; //表示便签修改的日期
    
    private boolean top = false; //表示便签是否置顶

    //下面就是所有私有属性的外部修改方法和获取方法
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }
    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public boolean isShow() {
        return isShow;
    }

    public void setShow(boolean isShow) {
        this.isShow = isShow;
    }

    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean isChecked) {
        this.isChecked = isChecked;
    }

    public boolean getTop() {
        return top;
    }

    public void setTop(boolean top) {
        this.top = top;
    }

    @Override
    public String toString() {
        return "ItemBean{" +
                "id='" + id + '\'' +
                ", title='" + title + '\'' +
                ", content='" + content + '\'' +
                ", date='" + date
                ;
    }

}
Toast的公用方法

主要负责Toast的弹出,防止多次调用Toast无法即时显示

import android.content.Context;
import android.widget.Toast;

public class ToastUtil {
    public static Toast mToast;
    public static void ShowMsg (Context context, String string) {
        if (mToast != null) {
            mToast.cancel();
        }
        mToast = Toast.makeText(context, string, Toast.LENGTH_SHORT);
        mToast.show();
    }
}
Date获取的公用方法

负责获取便签修改的时间

import java.util.Date;

public class DateUtil {

    public static Date getDateTime() {
        Date date = new Date(System.currentTimeMillis());
        return date;
    }
}
ListDataSave存储类

用SharedPreference存储便签的数据

通过创建ListDataSave这个类来综合实现存储功能

import android.content.Context;
import android.content.SharedPreferences;

import com.example.notebook.bean.ItemBean;

//注意需要在build.gradle文件中添加依赖
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

public class ListDataSave {
    private SharedPreferences preferences;
    private SharedPreferences.Editor editor;
    private static final String key = "javabean";
    private List<ItemBean> datalist;
    private String strJson;

    public ListDataSave(Context mContext, String preferenceName) {
        preferences = mContext.getSharedPreferences(preferenceName, Context.MODE_PRIVATE);
        editor = preferences.edit();
    }

    //给外部提供的传入数据的方法
    public void setDataList(List<ItemBean> datalist) {
        //主要用于第一次打开应用或者数据全部删除的情况,为其传入一个空值
        if (null == datalist || datalist.size() <= 0){
            editor.clear();
            editor.putString(key, null);
            editor.commit();
        } else {
            Gson gson = new Gson();
            //转换成json数据,再保存
            //因为SharedPreferences不支持List<ItemBean>类型的存储
            strJson = gson.toJson(datalist);
            editor.clear();
            editor.putString(key, strJson);
            editor.commit();
        }

    }

    //给外部提供的获取数据的方法
    public List<ItemBean> getDataList() {
        datalist=new ArrayList<>();
        strJson = preferences.getString(key, null);
        if (null == strJson) {
            return datalist;
        }
        Gson gson = new Gson();

        //从json数据转换回来,详细操作可以自己网上查找
        datalist = gson.fromJson(strJson, new TypeToken<List<ItemBean>>() {}.getType());
        return datalist;

    }
}

新建(详情)界面的实现

该界面主要是用于输入便签的标题和内容,以及返回输入的内容显示在主界面上,同时兼具返回修改便签的时间的功能

新建界面的布局文件如下:

<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:orientation="vertical">


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        tools:context=".NewTextActivity">

        <EditText
            android:id="@+id/tv_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:fontFamily="casual"
            android:hint="Title"
            android:textSize="30sp"
            android:textStyle="bold" />

        <EditText
            android:id="@+id/tv_content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="40dp"
            android:fontFamily="casual"
            android:gravity="start|left"
            android:hint="Content"
            android:textSize="20sp" />

    </LinearLayout>

    <LinearLayout
        android:id="@+id/btns"
        android:layout_width="390sp"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerInParent="true"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btn1"
            android:layout_width="180sp"
            android:layout_height="wrap_content"
            android:text="Save"
            android:textSize="30sp"
            android:textColor="@color/black"
            android:textStyle="bold"
            android:background="#00000000"
            android:fontFamily="casual"
            android:onClick="toSave"/>

        <Button
            android:id="@+id/btn2"
            android:layout_width="180sp"
            android:layout_height="wrap_content"
            android:layout_marginLeft="30sp"
            android:text="Back"
            android:textSize="30sp"
            android:textColor="@color/black"
            android:textStyle="bold"
            android:background="#00000000"
            android:fontFamily="casual"
            android:onClick="toBack" />

    </LinearLayout>

</RelativeLayout>

新建界面的java代码如下:

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;

import com.example.notebook.Util.DateUtil;
import com.example.notebook.Util.ToastUtil;
import com.example.notebook.bean.ItemBean;

import java.util.Date;
import java.util.List;

public class NewTextActivity extends AppCompatActivity {

    private List<ItemBean> mBeanList;

    private EditText mTitle;
    private EditText mContent;

    private String title;
    private String content;
    private Date date;
    private boolean top;

    private ItemBean mItemBean;
    private int id;

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

        initView();
        initData();
        initEvent();

    }

    //如果传入的数据不为空,则将传入的数据填入EditText中(实现便签的修改)
    private void initEvent() {
        if (mItemBean != null) {
            mTitle.setText(mItemBean.getTitle());
            mContent.setText(mItemBean.getContent());
        }
    }

    //接收传入的数据
    private void initData() {
        Intent intent = getIntent();
        mItemBean = (ItemBean) intent.getSerializableExtra("detail");
        id = mItemBean.getId();
        top = mItemBean.getTop();
    }

    //控件的绑定
    private void initView() {
        mTitle = (EditText) findViewById(R.id.tv_title);
        mContent = (EditText) findViewById(R.id.tv_content);
    }

    //保存按钮的方法实现,获取EditText中的数据并将其与时间等数据全部保存后传回主界面
    public void toSave(View view) {
        title = mTitle.getText().toString();
        content = mContent.getText().toString();
        date = DateUtil.getDateTime();

        ItemBean itemBean = new ItemBean();
        itemBean.setId(id);
        itemBean.setTop(top);
        itemBean.setTitle(title);
        itemBean.setContent(content);
        itemBean.setDate(date);

        Intent intent = new Intent(NewTextActivity.this, TextActivity.class);
        intent.putExtra("item",itemBean);
        ToastUtil.ShowMsg(NewTextActivity.this,"Text is saved successfully!");
        startActivity(intent);

    }
    
    //返回按钮的方法实现,一个简单的页面结束,不传回任何数据
    public void toBack(View view) {
        finish();
    }

}

适配器MyAdapter的实现

适配器MyAdapter的代码如下:

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.TextView;

import com.example.notebook.R;

import com.example.notebook.bean.ItemBean;

import java.text.SimpleDateFormat;
import java.util.List;

public class MyAdapter extends BaseAdapter {

    private List<ItemBean> mBeanList;
    private LayoutInflater mLayoutInflater;
    private Context mContext;
    private OnShowItemClickListener onShowItemClickListener;

    public MyAdapter(Context context, List<ItemBean> beanList){
        this.mContext = context;
        this.mBeanList = beanList;
        mLayoutInflater = LayoutInflater.from(mContext);
    }

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

    @Override
    public Object getItem(int position) {
        return mBeanList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        //使用ViewHolder减少findViewById方法的调用,增加运行速度
        ViewHolder viewHolder = new ViewHolder();
        
        //用convertView实现对View的复用
        if (convertView == null){
            convertView = mLayoutInflater.inflate(R.layout.base_list,parent,false);
            TextView tvTitle = convertView.findViewById(R.id.tv_title);
            TextView tvContent = convertView.findViewById(R.id.tv_content);
            //时间
            TextView tvDate = convertView.findViewById(R.id.tv_date);
            //选择框CheckBox
            CheckBox cb = convertView.findViewById(R.id.lv_cb);
            //表示置顶的标签图片
            ImageView img = convertView.findViewById(R.id.iv_img);

            viewHolder.tvTitle = tvTitle;
            viewHolder.tvContent = tvContent;
            viewHolder.tvDate = tvDate;
            viewHolder.cb = cb;
            viewHolder.img = img;

            convertView.setTag(viewHolder);

        }else {
            viewHolder = (ViewHolder) convertView.getTag();
        }

        final ItemBean itemBean = mBeanList.get(position);
        
        //判断是否显示CheckBox
        if (itemBean.isShow()) {
            viewHolder.cb.setVisibility(View.VISIBLE);
        } else {
            viewHolder.cb.setVisibility(View.GONE);
        }

        //判断是否显示置顶标签
        if (itemBean.getTop()){
            viewHolder.img.setVisibility(View.VISIBLE);
        } else {
            viewHolder.img.setVisibility(View.GONE);
        }

        //时间存储显示的格式
        SimpleDateFormat simpleDateFormat =new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
        String date = simpleDateFormat.format(itemBean.getDate());

        viewHolder.tvTitle.setText(itemBean.getTitle());
        viewHolder.tvContent.setText(itemBean.getContent());
        viewHolder.tvDate.setText(date);

        //CheckBox的点击监听,通过setChecked函数为item标记为选中或不选中状态
        viewHolder.cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (isChecked) {
                    itemBean.setChecked(true);
                } else {
                    itemBean.setChecked(false);
                }
                // 回调方法,将Item加入已选
                onShowItemClickListener.onShowItemClick(itemBean);
            }
        });
        // 必须放在监听后面
        viewHolder.cb.setChecked(itemBean.isChecked());

        return convertView;
    }

    class ViewHolder {
        TextView tvTitle;
        TextView tvContent;
        TextView tvDate;
        CheckBox cb;
        ImageView img;
    }

    public interface OnShowItemClickListener {
        public void onShowItemClick(ItemBean bean);
    }

    public void setOnShowItemClickListener(OnShowItemClickListener onShowItemClickListener) {
        this.onShowItemClickListener = onShowItemClickListener;
    }

}

MyAdapter适配器里面会处理主界面ListView需要显示的所有数据

主界面的实现

  • 主界面的上方是便签的显示区域,用ListView来实现
  • 右下角是便签的新增按钮,可以用来新增便签
  • 界面最下方有一个隐藏的编辑栏,初始是不可见的,在长按便签后才会弹出
主界面的基本功能

主界面的布局文件如下:

<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:orientation="horizontal"
    tools:context=".TextActivity">

    <ListView
        android:id="@+id/lv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/lay">

    </ListView>

    <LinearLayout
        android:id="@+id/lay"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_alignParentBottom="true"
        android:background="#00000000"
        android:orientation="horizontal"
        android:visibility="gone"
        //定义子视图的weight总和的最大值,方便进行等分处理
        android:weightSum="5">

        <TextView
            android:id="@+id/operate_back"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            //设置在父视图的权重
            android:layout_weight="1"
            android:gravity="center"
            android:paddingTop="5dp"
            android:paddingBottom="5dp"
            android:text="Back"
            android:textSize="22sp"
            //自行设置字体的样式
            android:fontFamily="casual"/>

        <TextView
            android:id="@+id/operate_select"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:paddingTop="5dp"
            android:paddingBottom="5dp"
            android:text="All"
            android:textSize="22sp"
            android:fontFamily="casual"/>

        <TextView
            android:id="@+id/invert_select"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:paddingTop="5dp"
            android:paddingBottom="5dp"
            android:text="Invert"
            android:textSize="22sp"
            android:fontFamily="casual"/>

        <TextView
            android:id="@+id/operate_top"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:paddingTop="5dp"
            android:paddingBottom="5dp"
            android:text="Top"
            android:textSize="22sp"
            android:fontFamily="casual"/>

        <TextView
            android:id="@+id/operate_delete"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:paddingTop="5dp"
            android:paddingBottom="5dp"
            android:text="Delete"
            android:textSize="22sp"
            android:fontFamily="casual"/>
    </LinearLayout>

    <Button
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        //设置按钮在父布局的右下角
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:text="new"
        android:textSize="30sp"
        android:textColor="@color/black"
        android:textStyle="bold"
        android:background="#00000000"
        android:fontFamily="casual"
        //设置按钮跳转到新增便签界面的函数
        android:onClick="toNewText"
        />

</RelativeLayout>

主界面的每个item的布局文件如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    android:paddingTop="5dp"
    android:paddingRight="10dp"
    android:paddingBottom="5dp">

    //表示置顶的标识图片,初始是隐藏的
    <ImageView
        android:id="@+id/iv_img"
        android:layout_width="130dp"
        android:layout_height="10dp"
        android:scaleType="centerCrop"
        android:src="@drawable/button_left"
        android:visibility="gone" />

    //标题
    <TextView
        android:id="@+id/tv_title"
        android:layout_width="200sp"
        android:layout_height="90sp"
        android:layout_marginLeft="10sp"
        android:layout_below="@+id/iv_img"
        android:ellipsize="end"
        android:maxLines="2"
        android:text="Title"
        android:textSize="40sp"
        android:textStyle="bold"
        android:fontFamily="casual"/>

    //日期
    <TextView
        android:id="@+id/tv_date"
        android:layout_width="match_parent"
        android:layout_height="30sp"
        android:layout_marginLeft="10sp"
        android:layout_toRightOf="@+id/tv_title"
        android:ellipsize="end"
        android:maxLines="1"
        android:text="Date"
        android:textSize="13sp"
        android:textStyle="normal"
        android:textAlignment="textEnd"
        android:fontFamily="casual"/>

    //内容
    <TextView
        android:id="@+id/tv_content"
        android:layout_width="match_parent"
        android:layout_height="70sp"
        android:layout_marginLeft="10sp"
        android:layout_toRightOf="@+id/tv_title"
        android:layout_below="@+id/tv_date"
        android:ellipsize="end"
        android:maxLines="2"
        android:text="Content"
        android:textSize="20sp"
        android:textStyle="normal"
        android:fontFamily="casual"/>

    //选择框
    <CheckBox
        android:id="@+id/lv_cb"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/tv_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"
        android:layout_alignParentTop="true" />

</RelativeLayout>
Button的跳转函数
public void toNewText(View view) {
    ItemBean itemBean = new ItemBean();
    Intent intent = new Intent(TextActivity.this, NewTextActivity.class);
    intent.putExtra("detail",itemBean);
    startActivity(intent);
}

考虑到将来点击便签进行修改时要复用新建的界面,故也进行空值的传输,方便一起管理

接收回传数据的函数
@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    setIntent(intent);
    displayIntentData();
}

其中的displayIntentData()函数如下:

private void displayIntentData() {
    Intent intent = getIntent();
    Bundle extras = intent.getExtras();
    if (extras != null) {
        mItemBean = (ItemBean) intent.getSerializableExtra("item");
        if (mItemBean.getId() == -1) {
            int position = 0;
            //新建的便签必须全部放在置顶便签的后面
            for (ItemBean itemBean : mBeanList) {
                if (itemBean.getTop()){
                    position++;
                }else {
                    break;
                }
            }
            mBeanList.add(position, mItemBean);
        }else {
            mBeanList.remove(mItemBean.getId());
            int position = 0;
            for (ItemBean itemBean : mBeanList) {
                //排序分为两种,修改的是置顶便签还是非置顶便签
                //核心思想是置顶优先,其次日期优先
                if (mItemBean.getTop()) {
                    if (itemBean.getDate().after(mItemBean.getDate()) && itemBean.getTop()){
                        position++;
                    }else {
                        break;
                    }
                }else {
                    if (itemBean.getDate().after(mItemBean.getDate()) || itemBean.getTop()){
                        position++;
                    }else {
                        break;
                    }
                }
            }
            mBeanList.add(position, mItemBean);
        }
    }else {
        ToastUtil.ShowMsg(this,"No data received");
    }
}
主界面绑定控件
private void initView() {
    mListView = findViewById(R.id.lv);
    lay = findViewById(R.id.lay);
    btn = findViewById(R.id.btn);
}
主界面从ListDataSave中获取数据并初始化
private void initData() {
    listDataSave = new ListDataSave(getApplicationContext(),"text");
    //selectList是CheckBox的选中列表
    selectList = new ArrayList<>();
    if (listDataSave.getDataList() != null) {
        mBeanList = listDataSave.getDataList();
        for (ItemBean itemBean:mBeanList) {
            itemBean.setChecked(false);
            itemBean.setShow(false);
        }
    }else {
        mBeanList = new ArrayList<>();
    }
}
主界面的操作事件
private void initEvent() {
    mMyAdapter = new MyAdapter(this, mBeanList);
    //为ListView设置适配器
    mListView.setAdapter(mMyAdapter);

    //ListView选中状态的批量处理
    MyAdapter.OnShowItemClickListener onShowItemClickListener = new MyAdapter.OnShowItemClickListener() {
        @Override
        public void onShowItemClick(ItemBean bean) {
            if (bean.isChecked() && !selectList.contains(bean)) {
                //如果已选中且选中列表中不包含,则添加
                selectList.add(bean);
            } else if (!bean.isChecked() && selectList.contains(bean)) {
                //如果未选中且选中列表中包含,则删去
                selectList.remove(bean);
            }
            listDataSave.setDataList(mBeanList);
        }
    };
    mMyAdapter.setOnShowItemClickListener(onShowItemClickListener);

    //ListView的点击事件监听
    mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            ItemBean itemBean = mBeanList.get(position);
            //如果处于长按的编辑状态
            if (isShow) {
                boolean isChecked = itemBean.isChecked();
                if (isChecked) {
                    itemBean.setChecked(false);
                } else {
                    itemBean.setChecked(true);
                }
                //刷新Adapter进行显示
                mMyAdapter.notifyDataSetChanged();
            }else { 
                //如果处于非编辑的显示状态
                itemBean.setId(position);
                Intent intent = new Intent(TextActivity.this,NewTextActivity.class);
                intent.putExtra("detail",itemBean);
                startActivity(intent);
            }
        }
    });
    
    //ListView的长按事件监听
    mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
        @Override
        public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
            //如果处于长按编辑状态
            if (isShow) {
                return false;
            } else {
                isShow = true;
                for (ItemBean bean : mBeanList) {
                    bean.setShow(true);
                }
                mMyAdapter.notifyDataSetChanged();
                showOperate();
                mListView.setLongClickable(false);
            }
            //返回true表示不再响应OnItemClickListener事件
            return true;
        }
    });

}

编辑栏的实现

编辑栏的布局文件见上方的主界面布局

编辑栏的实现代码
private void showOperate() {
    //进入、退出的动画效果
    lay.setVisibility(View.VISIBLE);
    btn.setVisibility(View.GONE);
    Animation anim1 = AnimationUtils.loadAnimation(this, R.anim.operate_in);
    Animation anim2 = AnimationUtils.loadAnimation(this, R.anim.operate_out);
    lay.setAnimation(anim1);
    btn.setAnimation(anim2);
    
    // 返回、删除、全选、反选和置顶按钮初始化及点击监听
    TextView tvBack = findViewById(R.id.operate_back);
    TextView tvDelete = findViewById(R.id.operate_delete);
    TextView tvSelect = findViewById(R.id.operate_select);
    TextView tvInvertSelect = findViewById(R.id.invert_select);
    TextView tvTop = findViewById(R.id.operate_top);
    
    tvBack.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (isShow) {
                selectList.clear();
                for (ItemBean bean : mBeanList) {
                    bean.setChecked(false);
                    bean.setShow(false);
                }
                mMyAdapter.notifyDataSetChanged();
                isShow = false;
                mListView.setLongClickable(true);
                dismissOperate();
            }
        }
    });
    
    tvSelect.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            for (ItemBean bean : mBeanList) {
                if (!bean.isChecked()) {
                    bean.setChecked(true);
                    if (!selectList.contains(bean)) {
                        selectList.add(bean);   
                    }
                }
            }
            mMyAdapter.notifyDataSetChanged();
        }
    });
    
    tvInvertSelect.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            for (ItemBean bean : mBeanList){
                if (!bean.isChecked()){
                    bean.setChecked(true);
                    if (!selectList.contains(bean)) {
                        selectList.add(bean);
                    }
                }else {
                    bean.setChecked(false);
                    if (!selectList.contains(bean)) {
                        selectList.remove(bean);
                    }
                }
            }
            mMyAdapter.notifyDataSetChanged();
        }
    });
    
    tvTop.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (selectList != null && selectList.size() > 0) {
                getTop();
            } else {
                ToastUtil.ShowMsg(TextActivity.this, "Please choose a text");
            }
        }
    });
    
    tvDelete.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (selectList != null && selectList.size() > 0) {
                getAlertDialog();
            } else {
                ToastUtil.ShowMsg(TextActivity.this, "Please choose a text");
            }
        }
    });
}
置顶功能的getTop()函数
private void getTop() {
    List<ItemBean> topList = new ArrayList<>();
    topList.addAll(mBeanList);
    //将选中列表中的含有的元素保留,其他全部删去
    //这样做能在置顶操作后保留选中标签原本的顺序,而不是选中的先后顺序
    topList.retainAll(selectList);
    for (ItemBean itemBean : topList) {
        itemBean.setTop(true);
    }
    mBeanList.removeAll(selectList);
    //将置顶列表中的元素添加到整个列表的开头
    mBeanList.addAll(0,topList);
    listDataSave.setDataList(mBeanList);
    mMyAdapter.notifyDataSetChanged();
}
删除功能弹出提示框的getAlertDialog()函数
private void getAlertDialog() {
    AlertDialog.Builder builder = new AlertDialog.Builder(TextActivity.this);
    //设置对话框的标题
    builder.setTitle("Warning");
    //设置对话框的内容
    builder.setMessage("Are you sure to delete these text?");
    //设置按钮
    builder.setPositiveButton("Sure", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            //点击确定按钮后进行删除操作
            mBeanList.removeAll(selectList);
            listDataSave.setDataList(mBeanList);
            mMyAdapter.notifyDataSetChanged();
            selectList.clear();
            dialog.cancel();
        }
    });
    builder.setNegativeButton("Back", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            dialog.cancel();
        }
    });
    AlertDialog alertDialog = builder.create();
    alertDialog.show();
}
隐藏编辑栏的函数
private void dismissOperate() {
    //绑定动画效果
    Animation anim1 = AnimationUtils.loadAnimation(TextActivity.this, R.anim.operate_out);
    Animation anim2 = AnimationUtils.loadAnimation(TextActivity.this, R.anim.operate_in);
    lay.setVisibility(View.GONE);
    btn.setVisibility(View.VISIBLE);
    lay.setAnimation(anim1);
    btn.setAnimation(anim2);
}
新建按钮和编辑栏的动画效果

进入的动画效果:

<set xmlns:android="http://schemas.android.com/apk/res/android">

    <translate
        android:fromYDelta="100%"
        android:toYDelta="0"
        android:duration="500">
    </translate>

</set>

退出的动画效果:

<set xmlns:android="http://schemas.android.com/apk/res/android">

    <translate
        android:fromYDelta="0"
        android:toYDelta="100%"
        android:duration="500">
    </translate>

</set>

其他功能的实现

重写返回键

  • 实现按两次返回键退出程序
  • 实现按返回键退出编辑栏编辑状态
@Override
public void onBackPressed() {
    if (isShow) {
        selectList.clear();
        for (ItemBean bean : mBeanList) {
            bean.setChecked(false);
            bean.setShow(false);
        }
        mMyAdapter.notifyDataSetChanged();
        isShow = false;
        mListView.setLongClickable(true);
        dismissOperate();
    } else {
        if (System.currentTimeMillis() - exitTime > 2000) {
            // 如果两次点击间隔大于2秒,则提示再次点击退出
            ToastUtil.ShowMsg(this, "Click twice to exit the application");
            exitTime = System.currentTimeMillis();
        } else {
            // 如果两次点击间隔小于2秒,则退出程序
            finish();
            System.exit(0);
        }
    }
}

重写生命周期函数,实现实时保存、刷新界面等功能

@Override
protected void onRestart() {
    super.onRestart();
    initView();
    initEvent();
}

//这里只是保险起见在所有函数里都写了保存方法
//实际应该只需要在一个开始一个停止的方法里写就可以了
@Override
protected void onStart() {
    super.onStart();
    listDataSave.setDataList(mBeanList);
}

@Override
protected void onResume() {
    super.onResume();
    listDataSave.setDataList(mBeanList);
}

@Override
protected void onStop() {
    super.onStop();
    listDataSave.setDataList(mBeanList);
}

小结

这次这个简易便签的开发看似比较简单,实际上涉及了很多listview和intent的知识

Listview实现的便签显示也可以用fragment来实现

详情界面和新建界面也可以分开,只是为了简洁就进行了复用

这个应用还有很多可以优化的地方,比如更多提示,置顶功能的取消,置顶功能逻辑的优化(现在对已经置顶的便签再进行置顶会将其置于所有置顶标签的最上方,而不会遵循置顶标签内按时间排序的规则,当然还有一个解决办法就是单独设计一个按时间顺序排列的按钮,不过我就没有继续去写了,要实现也比较简单),文本的高亮功能,整个标签界面的美化,应用主题的切换,等等……

之后我有时间也会将这些优化的功能慢慢添加进去,也当是一种学习进步了

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值