Android:实现安卓小程序-记事本(备忘录)的开发

开发者福利!热门AI工具限时免费用 购周边即赠Coding Plan Lite,Claude Code、Cursor等20+工具畅享,效率翻倍! 阅读详情

目录

1. 前言

2. 记事本功能需求

3. 部分关键代码解析及程序截图

3.1 记事本类的基本设计

3.2 记事本的数据存储设计

3.3 主界面的设计

3.4 记事本的编辑

3.5 记事本的背景色设置

3.6 记事本的闹铃功能

3.7 高级搜索

4. 总结


1. 前言

在学习安卓开发这门课程中期,学会了安卓开发的基本知识,比如,UI组件的应用,Intent的应用等等,同时期中作业为编写一个记事本程序,基于google的记事本demo, 但对于我本人来说,我更喜欢对代码从头到位都有自己的参与,于是我决定自己从头编写一个基本记事本的开发和记事本的相关扩展功能。

开发环境: Android Studio (API25以上)

2. 记事本功能需求

功能名称功能概述优先级
记事本基本功能对记事本的增删改最高
时间戳显示记事本的最近编辑时间的显示
高级搜索对记事本的查找
UI美化对用户界面的美化
记事本背景色的设置记事本背景色的设置
记事本闹钟设置记事本的闹钟功能设置

 

 

 

 

 

 

 

3. 部分关键代码解析及程序截图

3.1 记事本类的基本设计

Note.java

//序列化便于本地存储
public class Note  implements Serializable,Comparable{

    private String title;
    private String create_date;
    private String update_date;
    private String text;
    private String background;
    private String date_alarm;
    public Note(String title,String text) {
        //标题
        this.title = title;
        //创建日期
        this.create_date = new SimpleDateFormat("yyyy-MM-dd HH-mm").format(new Date());
        //更新日期
        this.update_date = this.create_date;
        //内容
        this.text = text;
        //背景色
        this.background = null;
        //闹钟日期
        this.date_alarm = "";
    }
}

3.2 记事本的数据存储设计

这里由于我当时暂未学习到SQLite的数据存储方式,采用了文件读写的方式来存储记事本的条目。

首先,在AndroidManifest.xml中设置允许程序读写文件的权限。

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.VIBRATE" />

将Note存储在哈希表内,再将哈希表的内容写入文件中,同时文件存储在内置的SD卡的路径下。

NoteManager.java

public class NoteManager {

    private Context mContext;
    private List<Note> list;
    private String root_file;
    public NoteManager(Context context){
        root_file = Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator+"NoteList";
        this.mContext = context;
        list = getNoteList();
    }
    
    //更新当前notes列表
    public void updateNoteList(List<Note> now_data){
        try {
            File file = new File(root_file);
            if (!file.exists()){
                file.createNewFile();
            }
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(root_file));
            oos.writeObject(now_data);
            oos.close();
        }
        catch (Exception e1){
            e1.printStackTrace();
        }
    }
}

3.3 主界面的设计

为了更好的用户体验,决定将用户设计设计成两种情况,一种为用户程序中暂无存储记事文件的情况下,一种为程序中已存有记事文件下。附上部分关键代码以及程序截图。

主界面-1主界面-2
//检查数据列表是否为空,如果为空,那么渲染blank_View
    public void emptyListCheck(){
        int number = 0;
        if(data!=null){
            number=data.size();
        }
        if(number == 0) {
            smlvMain.setVisibility(View.GONE);
            RelativeLayout empty = (RelativeLayout) findViewById(R.id.blank_view);
            empty.setVisibility(View.VISIBLE);
            fabMenu.setVisibility(View.VISIBLE);
        }else{
            smlvMain.setVisibility(View.VISIBLE);
            RelativeLayout empty = (RelativeLayout) findViewById(R.id.blank_view);
            empty.setVisibility(View.GONE);
        }
    }

这里设置了Note条目的菜单模块和空白模块,若数据列表为空,则隐藏条目菜单模块,显示空白模块,若不为空,则隐藏空白模块,显示菜单模块。

3.4 记事本的编辑

可以看到在主界面的右下角存在一个浮动窗口菜单,这里涉及到第三方依赖的添加:

com.getbase.floatingactionbutton.FloatingActionButton

res/layout/activity_main.xml

<com.getbase.floatingactionbutton.FloatingActionsMenu
            adroid:id="@+id/fab_menu"
            adroid:layout_width="wrap_content"
            adroid:layout_height="wrap_content"
            adroid:layout_alignParentRight="true"
            adroid:layout_alignParentBottom="true"
            fab:fab_labelStyle="@style/fab_label_style"
            app:fab_addButtonSize="mini"
            fab:fab_addButtonColorNormal="#5e96b9"
            fab:fab_addButtonColorPressed="@null"
            fab:fab_addButtonPlusIconColor="@color/black"
            adroid:layout_marginBottom="16dp"
            adroid:layout_marginRight="16dp">

            <com.getbase.floatingactionbutton.FloatingActionButton
                adroid:id="@+id/fab_add"
                adroid:layout_width="wrap_content"
                adroid:layout_height="wrap_content"
                fab:fab_colorNormal="@color/white"
                fab:fab_colorPressed="@color/green"
                fab:fab_title="新备忘录"
                fab:fab_size="mini"
                fab:fab_icon="@drawable/fab_add" />
            <com.getbase.floatingactionbutton.FloatingActionButton
                adroid:id="@+id/fab_exit"
                adroid:layout_width="wrap_content"
                adroid:layout_height="wrap_content"
                fab:fab_colorNormal="@color/white"
                fab:fab_colorPressed="@color/green"
                fab:fab_title="退出应用"
                fab:fab_size="mini"
                app:fab_icon="@drawable/fab_exit" />
</com.getbase.floatingactionbutton.FloatingActionsMenu>

这里可以看到对浮动菜单栏的xml设计,包含两种功能,新建Note和应用的退出,涉及到功能的逻辑代码这里就不赘述了。

新建Note后,可在主界面-2下的点击Note条目或左滑条目进入编辑模式。

编辑模式-1编辑模式-2

 

同时可以注意到两者编辑模式略有不同,在编辑模式-1下,可以做到修改,删除,背景色的设置,闹铃的添加,在编辑模式-2下仅仅能做到修改和删除。

左滑Note条目左滑部分关键代码:

首先需要新建一个滑动view类

public class MyScrollView extends ScrollView {
   private ScrollViewListener scrollViewListener = null;

    public MyScrollView(Context context) {
        super(context);
    }

    public MyScrollView(Context context, AttributeSet attrs,
                        int defStyle) {
        super(context, attrs, defStyle);
    }

    public MyScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setOnScrollListener(ScrollViewListener scrollViewListener) {
        this.scrollViewListener = scrollViewListener;
    }

    @Override
    protected void onScrollChanged(int x, int y, int oldx, int oldy) {
        super.onScrollChanged(x, y, oldx, oldy);
        if (scrollViewListener != null) {
            if (oldy < y && ((y - oldy) > 15)) {
                scrollViewListener.onScroll(y - oldy);
            } else if (oldy > y && (oldy - y) > 15) {
                scrollViewListener.onScroll(y - oldy);
            }
        }
    }
//这一部分参考了github某个开源项目
    public  interface ScrollViewListener{//dy Y轴滑动距离
        void onScroll(int dy);
    }
}

设置完毕后在xml文件中即可设置应用:

<com.zc.memo.view.MyScrollView
            adroid:id="@+id/sv_main"
            adroid:layout_width="match_parent"
            adroid:layout_height="wrap_content"
            adroid:background="@drawable/border_bottom_null">
            <LinearLayout
                adroid:layout_width="match_parent"
                adroid:layout_height="wrap_content"
                adroid:orientation="vertical">
                <com.baoyz.swipemenulistview.SwipeMenuListView
                    adroid:id="@+id/smlv_main"
                    adroid:layout_width="match_parent"
                    adroid:layout_height="match_parent"
                    adroid:descendantFocusability="blocksDescendants"
                    adroid:divider="@color/little_gray"
                    adroid:dividerHeight="1dp"
                    adroid:background="@null"
                    />
            </LinearLayout>
</com.zc.memo.view.MyScrollView>

接着在activity中,初始化滑动窗口及滑动窗口菜单。

 private void initView() {

        data = new NoteManager(context).getNoteList();
        smlvMain.setAdapter(new NoteAdapter(context,data));
        smlvMain.setMenuCreator(new MySwipeMenuCreator(context));
        smlvMain.setSwipeDirection(SwipeMenuListView.DIRECTION_LEFT);
        new ListViewUtil().setListViewHeightBasedOnChildren(smlvMain);
        svMain.setOnScrollListener(new MyScrollView.ScrollViewListener() {
            @Override
            public void onScroll(int dy) {
                if (dy > 0) {
                    //下滑
                    showOrHideFab(false);
                } else if (dy <= 0 ) {
                    //上滑
                    showOrHideFab(true);
                }
            }
        });
        viewListener();
        emptyListCheck();
    }

这里仅放上如何初始化滑动窗口的view,关于监听这个滑动窗口的逻辑代码在这里就不赘述了。

接下来讲一讲自定义NoteAdapter适配器来展示即使条目,首先继承BaseAdapter实现一个抽象类MyBaseAdapter<T>该类可读取List列表的数据。

public abstract class MyBaseAdapter<T> extends BaseAdapter {
    Context context;
    List<T> data;

    public MyBaseAdapter(Context context, List<T> data) {
        this.context = context;
        this.data = data;
    }

    @Override
    public Object getItem(int i) {
        return null;
    }

    @Override
    public long getItemId(int i) {
        return 0;
    }
    @Override
    public int getCount() {
        if(data==null) return 0;
        return data.size();
    }
}

接着在编写NoteAdapter类继承上类,初始化view, 将Note条目的各项信息展示出来,其中还自定义了一个viewHolder类,方便对xml文件中的各项组件进行绑定编辑。

public class NoteAdapter extends MyBaseAdapter<Note> {

    public NoteAdapter(Context context, List<Note> data) {
        super(context, data);
    }
    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        ViewHolder viewHolder;
        if(view==null){
            viewHolder=new ViewHolder();
            LayoutInflater inflater = LayoutInflater.from(context);
            view = inflater.inflate(R.layout.item_main_list, viewGroup, false);
            viewHolder.tv_lv_month = (TextView) view.findViewById(R.id.tv_lv_month);
            viewHolder.tv_lv_day= (TextView) view.findViewById(R.id.tv_lv_day);
            viewHolder.tv_lv_title = (TextView) view.findViewById(R.id.tv_lv_title);
            viewHolder.tv_lv_content = (TextView) view.findViewById(R.id.tv_lv_content);
            viewHolder.layout = (LinearLayout) view.findViewById(R.id.Linelayout);
            view.setTag(viewHolder);
        }
        else{
            viewHolder = (ViewHolder) view.getTag();
        }
        viewHolder.tv_lv_month.setText(data.get(i).getUpdate_date().split("-")[1]);
        viewHolder.tv_lv_day.setText(data.get(i).getUpdate_date().split("-")[2]);
        viewHolder.tv_lv_title.setText(data.get(i).getTitle());
        viewHolder.tv_lv_content.setText(data.get(i).getText());

        if(data.get(i).getBackground()!=null){
            viewHolder.layout.setBackgroundColor(Color.parseColor(data.get(i).getBackground()));
        }
        return view;
    }

    class ViewHolder{
        TextView tv_lv_month;
        TextView tv_lv_day;
        TextView tv_lv_title;
        TextView tv_lv_content;
        LinearLayout layout;
    }
}

3.5 记事本的背景色设置

进入到上述提到的编辑模式2中,也就是ContentActivity中,在其底部设置更改背景色功能。这里运用到了会话框的弹出AlertDialog。设置三种颜色。点击确认后,更改数据文件中相应记事条目Note的Background属性。接着进行调用onResume();使其重新载入数据文件中的记事条目得到背景色的更改。功能截图如下:

背景选项设置成功

部分关键代码如下:

ivSettings.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                AlertDialog.Builder builder = new AlertDialog.Builder(ContentActivity.this);
                builder.setIcon(R.drawable.fab_settings);
                builder.setTitle("选择一个背景色");
                //    指定下拉列表的显示数据
                final String[] colors = {"护眼色", "薰衣淡紫", "粉粉粉"};
                builder.setItems(colors, new DialogInterface.OnClickListener()
                {
                    @Override
                    public void onClick(DialogInterface dialog, int which)
                    {
                        Toast.makeText(ContentActivity.this, "选择的背景色为:" + colors[which], Toast.LENGTH_SHORT).show();
                        switch (which){
                            case 0:
                                new NoteManager(mContext).updateBackground(title,"#C7EDCC");
                                onResume();
                                break;
                            case 1:
                                new NoteManager(mContext).updateBackground(title,"#E6E6FA");
                                onResume();
                                break;
                            case 2:
                                new NoteManager(mContext).updateBackground(title,"#FC9D9A");
                                onResume();
                                break;
                            default:
                                break;
                        }

                    }
                });
                builder.show();
            }
        });

3.6 记事本的闹铃功能

该功能的实现思路为,为闹钟设置闹铃后,在该记事条目对应得ContentActvity的右上角会显示闹铃的时间,长按该闹铃时间戳则可删除该闹铃,同时闹铃到达指定时间后,会在程序中弹出提示框并且在手机通知栏也会有消息提醒。功能截图如下:

设置闹钟闹钟设置完成通知栏响应

首先是闹铃时间的设定,这里要对ContentActivity实现相应的设置日期时间的接口。

public class ContentActivity extends AppCompatActivity
        implements DatePickerDialog.OnDateSetListener, TimePickerDialog.OnTimeSetListener{

    @Override
    public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
        alarm_year=year;
        alarm_month=monthOfYear+1;
        alarm_day=dayOfMonth;
    }

    @Override
    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
        alarm_hour=hourOfDay;
        alarm_minute=minute;

        alarm=alarm_year+"/"+alarm_month+"/"+alarm_day+" "+alarm_hour+":"+alarm_minute;
        av.setText("Alert at "+alarm+"!");
        av.setVisibility(View.VISIBLE);

        Log.d("ContentActivity","alarm"+alarm);
        new NoteManager(mContext).updateAlarm(title,alarm);
        loadAlarm(alarm, title, 0);
        Toast.makeText(this,"Alarm will be on at "+alarm+" !",Toast.LENGTH_LONG).show();
    }


}

接着就是开始设置闹铃:

public void setAlarm(View v) {
        if(alarm.length()<=1) {
            //if no alarm clock has been set up before
            //show the current time
            Calendar c=Calendar.getInstance();
            alarm_hour=c.get(Calendar.HOUR_OF_DAY);
            alarm_minute=c.get(Calendar.MINUTE);

            alarm_year=c.get(Calendar.YEAR);
            alarm_month=c.get(Calendar.MONTH)+1;
            alarm_day=c.get(Calendar.DAY_OF_MONTH);
        }
        else {
            //show the alarm clock time which has been set up before
            int i=0, k=0;
            while(i<alarm.length()&&alarm.charAt(i)!='/') i++;
            alarm_year=Integer.parseInt(alarm.substring(k,i));
            k=i+1;i++;
            while(i<alarm.length()&&alarm.charAt(i)!='/') i++;
            alarm_month=Integer.parseInt(alarm.substring(k,i));
            k=i+1;i++;
            while(i<alarm.length()&&alarm.charAt(i)!=' ') i++;
            alarm_day=Integer.parseInt(alarm.substring(k,i));
            k=i+1;i++;
            while(i<alarm.length()&&alarm.charAt(i)!=':') i++;
            alarm_hour=Integer.parseInt(alarm.substring(k,i));
            k=i+1;i++;
            alarm_minute=Integer.parseInt(alarm.substring(k));
        }

        new TimePickerDialog(this,this,alarm_hour,alarm_minute,true).show();
        new DatePickerDialog(this,this,alarm_year,alarm_month-1,alarm_day).show();
    }

同样的,监听该view,若被响应则调用onResume();

长按删除闹铃功能这里就不再赘述了,接下来来详细讲解如何调用通知栏的通知功能。部分关键代码:

private void loadAlarm(String alarm, String title, int days) {
        int alarm_hour=0;
        int alarm_minute=0;
        int alarm_year=0;
        int alarm_month=0;
        int alarm_day=0;

        int i=0, k=0;
        while(i<alarm.length()&&alarm.charAt(i)!='/') i++;
        alarm_year=Integer.parseInt(alarm.substring(k,i));
        k=i+1;i++;
        while(i<alarm.length()&&alarm.charAt(i)!='/') i++;
        alarm_month=Integer.parseInt(alarm.substring(k,i));
        k=i+1;i++;
        while(i<alarm.length()&&alarm.charAt(i)!=' ') i++;
        alarm_day=Integer.parseInt(alarm.substring(k,i));
        k=i+1;i++;
        while(i<alarm.length()&&alarm.charAt(i)!=':') i++;
        alarm_hour=Integer.parseInt(alarm.substring(k,i));
        k=i+1;i++;
        alarm_minute=Integer.parseInt(alarm.substring(k));

        Intent intent = new Intent(ContentActivity.this, OneShotAlarm.class);
        intent.putExtra("alarm_title",title);
        PendingIntent sender = PendingIntent.getBroadcast(
                ContentActivity.this, 0, intent, 0);

        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(System.currentTimeMillis());

        Calendar alarm_time = Calendar.getInstance();
        alarm_time.set(alarm_year,alarm_month-1,alarm_day,alarm_hour,alarm_minute);

        AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
        am.set(AlarmManager.RTC_WAKEUP, alarm_time.getTimeInMillis(), sender);
    }

在上述关键代码中,可以看到在loadAlarm函数中使用了Intent来传递闹铃时间的消息给OneShotAlarm类,且是再闹铃设置完成后,构建一个PendingIntent广播,通过AlarmManager在时间到达指定的闹铃事件后,发出广播,接收到广播的OneShotAlarm类做出回应,在通知栏显示通知。

这里要先在AndroidManifest.xml设置广播接收器:

<receiver
            android:name=".utils.OneShotAlarm"
            android:process=":remote" />

接下来让我们看看OneShortAlarm类,当OneShotAlarm收到广播后,则构建通知消息并在通知栏显示。同时该类中设置了通知消息显示的内容,有该记事本程序名,记事条目的标题,该记事创建时间戳以及记事内容。通过构造Notification类实现该消息。部分关键代码如下:

public class OneShotAlarm extends BroadcastReceiver {
    private String alarm_title;
    @Override
    public void onReceive(Context context, Intent intent) {
        alarm_title=intent.getStringExtra("alarm_title");
        Toast.makeText(context,"Time UP!",Toast.LENGTH_LONG).show();
        Vibrator vb =(Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
        vb.vibrate(300);

        showNotice(context);
    }
    private void showNotice(Context context) {

        Intent intent=new Intent(context,ContentActivity.class);
        Note record = new NoteManager(context).get(alarm_title);
        new NoteManager(context).deleteAlarm(alarm_title);
        PendingIntent pi=PendingIntent.getActivity(context,0,intent,PendingIntent.FLAG_UPDATE_CURRENT);
        NotificationManager manager=(NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
        Notification notification=new NotificationCompat.Builder(context)
                .setContentTitle(record.getCreate_date())
                .setContentText(record.getText())
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.mipmap.ic_launcher)
                .setLargeIcon(BitmapFactory.decodeResource(context.getResources(),R.drawable.icon))
                .setContentIntent(pi)
                .setAutoCancel(true)
                //.setStyle(new NotificationCompat.BigTextStyle().bigText(record.getMainText()))
                .setLights(Color.GREEN,1000,1000)
                .build();
        manager.notify(0,notification);
    }

}

3.7 高级搜索

在早先就有了解到一些动态搜索框现成的源码,这里则使用了一些网上开源的搜索框控件的使用。

首先需要添加第三方依赖,'com.quinny898.library.persistentsearch:library:1.1.0-SNAPSHOT'就可以使用了。该搜索框还支持语音输入识别,但这里因为在设计时并没有考虑语音识别功能,所以这里就弃用了该功能。

附上功能截图:

<com.quinny898.library.persistentsearch.SearchBox
        android:layout_width="wrap_content"
        android:id="@+id/searchbox"
        android:layout_height="wrap_content"/>

设置完成后,需要对显示搜索到的记事条目,内容,及搜索到的记事条目总和进行显示,这里布局设计如下:

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        <ListView
            android:id="@+id/history_lis_search"
            android:layout_marginLeft="20dp"
            android:layout_marginRight="20dp"
            android:layout_marginBottom="5dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/border_bottom_null">
        </ListView>
    </LinearLayout>

    <ListView
        android:id="@+id/content_lis_search"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/border_bottom_null">
    </ListView>

    <TextView
        android:id="@+id/bottom_search"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:gravity="center_horizontal" />

接着则是代码的实现,实现该功能代码网上有很多现成的实例,这里实现起来并没有看起来的那么复杂,稍微学习后则可写出自己想要的代码:

public class SearchActivity extends AppCompatActivity {
    private Context mContext;
    private SearchBox sbSearch;
    private TextView tvBottom;
    //数据列表
    private List<Note> listSearch;
    //结果列表
    private List<Note> listResult;
    private ListView mSearchResult ;
    private SearchAdapter mResultAdapter ;

    private ListView mHistory ;
    private HistoryAdapter mHistoryAdapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_search);
        mContext = this;
        bindViews();
    }

    @Override
    protected void onResume() {
        super.onResume();
        initData();
        init();
        initSearchView();
        initHistory();
        updateBottom();
    }

    private void initData() {
        //本地可供检索数据获取,每次resume就要重新渲染
        listSearch = new NoteManager(mContext).getNoteList();
    }

    private void bindViews() {
        mSearchResult = (ListView) findViewById(R.id.content_lis_search);
        sbSearch = (SearchBox) findViewById(R.id.searchbox);
        tvBottom = (TextView) findViewById(R.id.bottom_search);
        mHistory = (ListView) findViewById(R.id.history_lis_search);
    }

    public void init(){
        listResult = new ArrayList<>();
        mResultAdapter = new SearchAdapter(SearchActivity.this, listResult);
        mSearchResult.setAdapter(mResultAdapter);
        mSearchResult.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                manageKeyBoard();
                NoteManager noteManager=new NoteManager(mContext);
                InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
                boolean isOpen = imm.isActive();
                if (isOpen) imm.hideSoftInputFromWindow(view.getWindowToken(), 0); //强制隐藏键盘
                noteManager.ItemClick(listResult.get(position));
            }
        });
    }

    private void initSearchView(){
        sbSearch.enableVoiceRecognition(this);
        sbSearch.setMenuListener(new SearchBox.MenuListener(){
            @Override
            public void onMenuClick() {
                reBack();
            }
        });
        sbSearch.setSearchListener(new SearchBox.SearchListener(){

            @Override
            public void onSearchOpened() {
                //Use this to tint the screen
            }
            @Override
            public void onSearchClosed() {
                //Use this to un-tint the screen
            }
            @Override
            public void onSearchTermChanged(String term) {
                search(term);
                updateBottom();

                if(listResult.size()==0){
                    mHistory.setVisibility(View.VISIBLE);
                }else {
                    mHistory.setVisibility(View.GONE);
                }
            }
            @Override
            public void onSearch(String searchTerm) {
                search(searchTerm);
                saveHistory(searchTerm);
                initHistory();
                updateBottom();
            }
            @Override
            public void onResultClick(SearchResult result) {
                //React to a result being clicked
            }
            @Override
            public void onSearchCleared() {
                //Called when the clear button is clicked
            }

        });
    }

    private void initHistory(){

        final List<String> history = getHistory();
        mHistoryAdapter = new HistoryAdapter(SearchActivity.this,history);
        mHistory.setAdapter(mHistoryAdapter);
        mHistory.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                sbSearch.populateEditText(history.get(position));
                sbSearch.setSearchString(history.get(position));
            }
        });

    }

    private void search(String newText){

        //若搜索内容为空
        if(newText.isEmpty()){
            listResult.clear();
        }
        else{

            for (Note note : listSearch) {//开始搜索
                //搜索内容搜索到相关
                if (note.getTitle().contains(newText) || note.getText().contains(newText)) {

                    if(listResult.indexOf(note)==-1) {//且 结果集内不含有此内容
                        listResult.add(note);
                    }
                }else{
                    //搜索内容搜索不到相关 检测是否之前有加入结果集 有则删除
                    if(listResult.indexOf(note)!=-1) {
                        listResult.remove(note);
                    }
                }
            }
        }
        mResultAdapter.notifyDataSetChanged();
    }

    private ArrayList<String> getHistory() {
        SharedPreferences reader = getSharedPreferences("history", MODE_PRIVATE);
        String data = reader.getString("data_history", "");
        ArrayList<String> history = new ArrayList<>();
        String [] get=  data.split("\\|");
        for( String str:get){
            if(! history.contains(str) && !StringUtil.isEmpty(str)){
                history.add(str);
            }
        }
        return history;
    }

    private void saveHistory(String s){
        StringBuilder sb = new StringBuilder();
        SharedPreferences.Editor editor = getSharedPreferences("history",MODE_PRIVATE).edit();
        for (String str: getHistory()){
            sb.append(str);
            sb.append("|");
        }
        sb.append(s);
        editor.putString("data_history",sb.toString());
        editor.apply();
    }

    private void updateBottom(){
        if(sbSearch.getSearchText().trim().isEmpty()){
            gone_Bottom();
            return;
        }
        tvBottom.setVisibility(View.VISIBLE);
        tvBottom.setText("找到了 "+ listResult.size() +" 条记录");

    }
    private void gone_Bottom(){
        TextView bottom = (TextView) findViewById(R.id.bottom_search);
        bottom.setVisibility(View.GONE);
    }

    private void reBack(){
        finish();
        overridePendingTransition(R.anim.in_from_left, R.anim.out_from_right);
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        switch(keyCode){
            case KeyEvent.KEYCODE_BACK:
                finish();
                overridePendingTransition(R.anim.in_from_left, R.anim.out_from_right);
                break;
        }
        return super.onKeyUp(keyCode, event);
    }
    private void manageKeyBoard() {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
    }
}

4. 总结

这次完成该记事本的完成算是对先前学到知识的应用以及进阶,如Intent意图的使用,adapter适配器的使用,等等,对其掌握有了更进一步的提高,同时对程序UI美化和程序数据存储有了全新的了解与把握。也了解到市面上有很多开源的控件可供使用,还需要学习的东西还有很多很多。谢谢。

参考资料:

https://blog.csdn.net/zhouchen1998/article/details/83628218

https://blog.csdn.net/sf_zhang26/article/details/54803691

https://blog.csdn.net/zhouchen1998/article/details/82995526 

本文作者:林嘉伟

原文链接:https://blog.csdn.net/DieForCode/article/details/90602224

安卓开发实例———记事本 1.项目介绍 2.开发过程 3.实现效果 4.遇到的问题 阅读详情

相关推荐

Android实现安卓小程序-记事本(备忘录)开发android原生开发前景

private List list; private String root_file; public NoteManager(Context context){ root_file = Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator+“NoteList”; this.mContext = context; list = getNoteList(); } //更新当前notes列表 public void

m0_66264588的博客 1945

Android备忘录模式实现及源代码解析

通过创建备忘录类、发起人类和管理者类,并在Activity的生命周期方法中保存和恢复状态,我们可以方便地实现数据的保存和恢复。在Android开发中,我们可以将发起人看作是需要保存状态的对象(如Activity或Fragment),备忘录是该对象的内部状态的快照,管理者则可以是一个单例类或其他适当的组件。备忘录模式是一种行为设计模式,它允许在不破坏封装性的前提下捕获对象的内部状态,并在需要时恢复。在Android开发中,备忘录模式可以用于保存和恢复应用程序的状态或数据。

CyberLynxX的博客 685

Android实现安卓小程序-记事本(备忘录)开发,2021BTAJ面试真题详解

public Object getItem(int i) { return null; } @Override public long getItemId(int i) { return 0; } @Override public int getCount() { if(data==null) return 0; return data.size(); } } 接着在编写NoteAdapter类继承上类,初始化view, 将Note条目的各项信息展示出来,其中还自定义了一个viewHolder类,方便对xm

m0_65511948的博客 606

Android实现安卓小程序-记事本(备忘录)开发,教你如何增加拿到BAT大厂offer几率

各行各样都会淘汰一些能力差的,不仅仅是IT这个行业,所以,不要被程序猿是吃青春饭等等这类话题所吓倒,也不要觉得,找到一份工作,就享受安逸的生活,你在安逸的同时,别人正在奋力的向前跑,这样与别人的差距也就会越来越遥远,加油,希望,我们每一个人,成为更好的自己。点击此处,与我们一起交流学习文件夹有以下学习笔记,自行下载!BAT大厂面试题、独家面试工具包,资料免费领取,包括 数据结构、Kotlin、计算机网络、Framework源码、数据结构与算法、小程序、NDK、Flutter,

2301_77033340的博客 2144

Android手机开发课程设计之记事本

一、需求分析 1.1业务需求分析 近年来,随着生活节奏的加快,工作和生活的双重压力全面侵袭着人们,如何避免忘记工作和生活中的诸多事情而造成不良的后果就显得非常重要。为此我们开发一款基于Android系统的简单记事本,其能够便携记录生活和工作对诸多事情,从而帮助人们有效地进行时间管理。 1.2功能需求分析 本记事本项目希望可以开发出一款符合用户生活工作习惯的简单应用,能够满足用户的各方面需求,可以对记事进行增加、查看、修改和删除,要求功能完善丰富并且具有良好的用户界面和交互体验。 二、项目设计 2.1功能模块

qq_44111805的博客 1万+

Android实现安卓小程序-记事本(备忘录)开发,面试建议

外链图片转存中…(img-8lJy8sSQ-1712449754346)][外链图片转存中…(img-Q4gWWwDM-1712449754347)][外链图片转存中…(img-yXqz0hr2-1712449754347)][外链图片转存中…(img-1XBb0h2e-1712449754347)][外链图片转存中…(img-G90VbLdV-1712449754348)]

2401_84149845的博客 1558

Android开发————简易备忘录(一)

实验内容 1. android 开发环境搭建, 开发工具可以是eclipse+adt 或android studio。 2. 按照实验课要求制作对应的app小程序实现按钮添加备忘录。 3. 编写并提交实验报告。 实验步骤 (1)main_activity.xml代码: &lt;?xml version="1.0" encoding="utf-8"?&gt; &lt;LinearLa...

Fly_鹏程万里 1万+

个人事务备忘录管理微信小程序

UniApp是一款使用Vue.js开发所有前端应用的框架,能够同时在iOS、Android、H5、小程序等多个平台上运行;所以本系统可以是一个安卓app,也可以是微信小程序系统包括以下功能:备忘录 管理个人事务 记事本 事务分类 日记编写 后台管理数据背景: 该项目是一个多平台跨端应用,旨在提供一个集备忘录、个人事务管理、记事本、事务分类、日记编写和后台数据管理功能于一体的系统。通过使用UniApp框架,可以实现在iOS、Android、H5、微信小程序等多个平台上运行。

csbysj2020 1056

Android手势解锁备忘录App开发实战(Java实现

Android原生并未提供标准的手势锁组件,因此必须通过继承View类实现自定义控件。该控件需完成三大任务:绘制九宫格、捕获触摸事件、追踪并可视化用户绘制路径。自定义视图的核心在于方法的实现。我们需要预先计算九个控制点的坐标,并根据当前状态绘制不同样式的状态圈(空心/实心/错误色)和连接线。// 存储9个控制点坐标// 已选中点索引// 圆点半径@Overriderow < 3;

weixin_31688273的博客 1201

android小程序备忘录开发代码,从零开始写微信小程序--写一个备忘录【二】

接着上次的文章,开始这个项目须要简单构思一下有什么页面,每一个页面有些什么。这个小程序是这样的:javascript主页面有一条条的备忘录记录,右下角有个新建笔记的按钮,主界面下方有toBar,能够进行页面的切换。html进入新建笔记的页面后有一个输入框,上方有个Editor的按钮,点击能够隐藏或者显示。右上方有个删除的按钮,能够删除该条笔记。java第一步是首页的代码,由于是使用cells组件,...

weixin_36143786的博客 1187

基于Android系统的个人记账备忘录系统uniapp-hbuiderx 小程序

本个人记账备忘录采用编程语言Java,开源框架SSM实现系统的架构,系统的数据采用数据库MySQL进行存储。管理员具有首页、个人中心、用户管理、支出分类管理、收入类型管理、当日收入管理、当日支出管理、系统管理。个人记账备忘录能够处理面向用户的大多数事务,它包括了服务管理等工作,该系统采用了MVC设计模式和使用了数据库MySQL从而开发出个人记账备忘录,目的是方便用户和提高管理员的效率。(3)用户在个人记账备忘录上操作不能够过于复杂,不同的用户有着不同的操作权限,需要在用户表中加以区分。

QQ188083800的博客 1227

Android Studio AI记账小程序

这是一个实现了AI客服的安卓小系统

cjwdjjw的博客 809

android小程序备忘录,撸一个会话备忘录小程序

最终实现的撮图:目的: 借助小程序记录微信日常会话中的重要信息, 数据在本地缓存中操作,纯前端无后台,不用担心信息泄露问题。步骤:1、注册微信小程序,获取appid,传送门https://mp.weixin.qq.com2、下载新版微信开发者工具,新建备忘录项目,填写appid,确定后自动生成初始化代码3、我的项目结构:+-- assets //静态文件夹| +-- font...

weixin_39957312的博客 710

Android备忘录

在网上找到的一个实例:一个基于安卓的带事件提醒的备忘录小程序源码,该资源包含一个简易备忘录的全部代码,能实现基本功能:添加、搜索、删除、编辑事项,闹铃提醒,分享内容等。本源码默认编码GBK编译版本4.4.2

772

Android开发————简易备忘录(三)

实验内容 1. android 开发环境搭建, 开发工具可以是eclipse+adt 或android studio。 2. 按照实验课要求制作对应的app小程序实现按钮添加备忘,并且包含日期时间。 3. 编写并提交实验报告。 实验步骤 1.部分代码如下: &lt;Button android:id="@+id/b1" android:layout_width=...

Fly_鹏程万里 4194

微信小程序记事本Python项目PHP源代码Java安卓APP设计asp.net微信小程序nodejs

JAVA是一种建立在通用语言上的程序构架,能被用于一台Web服务器来建立强大的Web应用程序。java提供许多比现在的Web开发模式强大的的优势。java是把基于通用语言的程序在服务器上运行。不像以前的java即时解释程序,而是将程序在服务器端首次运行时进行编译。java构架是可以用Microsoft(R)公司最新的产品 Visual Studio开发环境进行开发,WYSIWYG(What You See Is What You Get所见即为所得)的编辑。这些仅是java。

itszkt的博客 416
头铁才学编程
博客等级 码龄9年 35粉丝 1原创
评论 26
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值