Android开发 - PsyDuck说明书
Android开发 - PsyDuck说明书
- 1 设计任务书
- 2 本人任务
- 3 项目背景
- 4 需求分析
- 5 技术路线和实现方案
- 6 数据库设计
- 7 方法说明
- 8 总体设计
- 9 详细设计
- 9.1PsyDuck Music初始化页面
- 9.2PsyDuck Music登录页面
- 9.3PsyDuck Music项目介绍页面
- 9.4 PsyDuck Music作者信息页面
- 9.5 PsyDuck Music用户注册页面
- 9.6 PsyDuck Music“我的”页面
- 9.7 PsyDuck Music“电台”页面
- 9.8 PsyDuck Music歌曲查询页面
- 9.9 PsyDuck Music歌曲播放页面
- 9.10 PsyDuck Music动态发布页面
- 9.11 PsyDuck Music动态查询页面
- 9.12 PsyDuck Music信息查看页面
- 10 工作总结
- 11 附录
1 设计任务书
众所周知,随着移动互联网的发展,我国在线音乐类APP发展迅猛,不断受到资本热捧,如今在线音乐类APP开发功能得到了极大的丰富,除了基本的听歌搜索功能,还有听歌识曲、铃声剪辑、彩铃专区、在线KTV唱歌等功能,满足了用户对音乐的大部分需求。如今,消费不断升级,其融入了社交属性,和短视频、直播等时下最热门的内容相结合,对用户的吸引力进一步提升,用户规模持续增长,行业前景受到看好。
音乐是这个世界上最美好的东西,给人艺术的愉悦,智慧的启迪,音乐之美无处不在。本人认为音乐播放器具有一定的市场,并对当下较好的音乐app进行研究分析,进行了页面设计和需求分析,提出了系统的整体设计目标解决方案。并根据需求分析对系统做了概要设计和数据库设计,并详细探讨了几个关键功能模块的技术实现。
2 本人任务
本组课题:PusDuckMusic App
小组分工:cs(代码编写)
cs (UI设计)
感谢帮助:yjd(PsyDuck手绘)
ahj(部分图片处理)
3 项目背景
随着科技发展和人民生活质量的提高,大众对于音乐的需求越来越大。随着数码产品的普及,听歌渐渐成为人们生活的一部分。互联网给我们的生活带来很大的便利,我们可以在线听歌,后来随着技术的发展,大部分开始使用软件听歌,在随后随着手机的发展互app软件也可以满足我们听歌的需求。然而,纵观如今所有在线音乐类APP,大都有曲库和评论推送功能,同质化功能严重。
因此,PsyDuck Music旨在做好细节,利用简洁的页面,精简的功能吸引用户。同时,PsyDuck Music利用场景化的功能开发,细化丰富的场景,让音乐有了情感和温度,让用户找到内心情绪、情感的寄托,从而找到自己,增加音乐APP的忠诚度。
4 需求分析
本项目自行构思得出,由个人独立编写程序研究。本项目的目的是开发一个可以播放主流的音乐文本格式的播放器。设计的主要实现功能是播放MP3等格式的音乐文件,并且能控制播放,暂停,停止,歌曲列表文件的管理操作,在线播放,读取存储卡播放等多种播放控制,界面简明,操作简单。 除此之外,用户还可以在PsyDuck平台上发表动态,搜索并关注自己喜欢的音乐人,拓展朋友圈,了解音乐文化。如图4-1所示,我将从基本控制、友好性、界面和可靠性4个方面对PsyDuck Music进行需求分析。
4.1PsyDuck播放器的基本控制要求
- 顺序播放:点击播放列表中的歌曲进行“顺序播放”按钮,点击“查看更多”标识可显示更多的歌曲信息,包括所含歌曲信息和演唱歌手等。
- 暂停播放:当歌曲正在播放时,点击歌曲播放界面下方的“暂停播放”按钮,停止当前播放的歌曲。
- 下一首播放:当歌曲正在播放时,点击歌曲播放界面下方的“下一首播放”按钮,暂停当前播放的歌曲,同时播放歌单中下一首歌曲。
4.2PsyDuck播放器友好性需求
- 视觉外观:使用PsyDuck Music时,播放器根据歌曲文件的特性,通过视觉外观动态显示,外观优美。
- 系统稳定:使用PsyDuck Music时能显示播放器的状态,是播放还是暂停,播放音乐时稳定。
- 后台播放:使用PsyDuck Music时,能够进行后台播放。
- 播放模式:播放音乐时,更改播放模式能够顺利地、快速地、无差错地完成模式的转换。
4.3PsyDuck Music 界面需求
PsyDuck Music界面要求布局合理,颜色舒适,控制按钮友好;为了较少开发工程量,可以借鉴现在流行的播放器的皮肤作为播放器的界面目标需求。
4.4PsyDuck Music可靠性需求
PsyDuck Music系统能持续运行,不影响其他程序的使用,不多占用内存,不会造成死机等问题。
5 技术路线和实现方案
5.1系统概况
PsyDuck Music系统基于android平台和SQLITE数据库,并调用多种API端口,利用统一的命名规则对组件和变量进行命名。安卓手机只需装上我们的客户端,并成功注册认证,登录系统后便能享受音乐。下面介绍一下PsyDuck Music系统所使用的技术。
-
多种控件的使用
在PsyDuck Music系统中,我通过调用layout中的不同的控件,比如简单的TextView、ListView、EditText、Combox、Button、SeekBar等,高级控件例如ExpendListView、ImageViewFactory。除此之外,在此基础上,我还参考了网上的资料,学习和模仿了ViewPagerAdapter、SearchView的编写,实现了流畅的轮播图和歌曲搜索功能,通过这么些控件的调用来使得UI效果变得更加的符合用户的审美需求。 -
SQLite数据库
SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。SQLite的设计目标是嵌入式的,而且已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中只需要几百k的内存,能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟Java语言相结合,拥有 ODBC接口,比起Mysql、PostgreSQL处理速度更快。 -
C/S架构
PsyDuck Music使用俩层C/S架构,应用服务器运行数据负荷较轻。一旦服务器程序被启动,就随时等待响应客户程序发来的请求;PsyDuck Music程序运行在用户自己的手机上,对应于数据库服务器,当需要对数据库中的数据进行任何操作时,PsyDuck Music就自动地寻找服务器程序,并向其发出请求,服务器程序根据预定的规则作出应答,送回结果,服务器运行数据负荷较轻。除此之外,数据的储存管理功能较为透明。
5.2技术实现
本系统采用俩层C/S架构,服务器负责数据的管理,客户机负责完成与用户的交互任务。
在C/S结构中,PsyDuck Music分为两部分:服务器部分和客户机部分。服务器部分是多个用户共享的信息与功能,执行后台服务,如控制共享数据库的操作等;客户机部分为用户所专有,负责执行前台功能,在出错提示、在线帮助等方面都有强大的功能,并且可以在子程序间*切换。
PsyDuck Music系统的全局架构如下图5-2所示:
5.3开发环境
- 开发工具:Eclipse
- 集成开发环境(IDE):Eclipse+ADT+JDK8+Android SDK
- 开发语言:java
- 操作系统:Windows 10
- 数据库:SQLLite
6 数据库设计
PsyDuck Music系统的数据库设计详情见下。
表格 6-1 User表
User表 | 属性名 | 数据类型 | 长度 | 备注 |
---|---|---|---|---|
Uid | Varchar | 10 | 非空 | |
Upass | Varchar | 16 | 非空 | |
Udate | Data | 非空 |
表格 6-2 Song表
Song表 | 属性名 | 数据类型 | 长度 | 备注 |
---|---|---|---|---|
Sid | Varchar | 50 | 非空 | |
Sname | Varchar | 16 | 非空 | |
Singer | Varchar | 50 | 非空 | |
Sdate | Data | 非空 | ||
Stime | Time | 非空 |
表格 6-3 Ssheet表
Ssheet表 | 属性名 | 数据类型 | 长度 | 备注 |
---|---|---|---|---|
Ssid | Varchar | 10 | 非空 | |
Ssname | Varchar | 16 | 非空 | |
Uid | Varchar | 50 | 非空 | |
Ssdate | Timestamp | 非空 |
表格 6-4 Prend表
Prend表 | 属性名 | 数据类型 | 长度 | 备注 |
---|---|---|---|---|
Ud | Varchar | 10 | 非空 | |
Pid | Varchar | 16 | 非空 | |
Pitem | Varchar | 500 | 非空 | |
Pdate | Timestamp | 非空 |
7 方法说明
表格 7-1 PsyDuck Music系统方法说明表
方法名 | 功能 | 备注(须输入的参数) |
---|---|---|
Csnb | 定义了Csnb的类 | 无 |
Fans | 定义了Fans的类 | 无 |
Find. setSearchWay | 设置搜索方法 | SearchView.SearchWay< String> |
Find. initListView | 初始化歌曲库 | 无 |
Find.MyOnClickListener | 定义了切换歌曲页面的内部类 | 无 |
FrameTabActivity | 主菜单切换 | 无 |
Guanzhu | 定义了Guanzhu的类 | 无 |
MusicService.play | 播放音乐 | String path |
MusicService.pause | 暂停播放音乐 | 无 |
MusicService.replay | 查询播放音乐 | 无 |
MusicService.stop | 停止播放音乐 | 无 |
MusicService.getMusicLength | 获取资源文件长度 | 无 |
MusicService. getCurrentProgress | 获取当前进度 | 无 |
NormalActivity | 定义了NormalActivity的类 | 无 |
Prend | 定义了Prend的类 | 无 |
PsyDuckIntroduction | 定义了PsyDuckIntroduction的类 | 无 |
Prend.setMessage | 发布动态 | String prendItem |
PsyDuckItemActivity | 定义了PsyDuckItemActivity的类 | 无 |
PsyDuckItemActivity.logout | 退出登录 | String uid |
PsyDuckMusicActivity | 定义了PsyDuckMusicActivity的类 | 无 |
PsyDuckMusicActivity.login | 用户登录 | String uid,String upass |
PsyDuckMusicActivity.rememberMe | 记住我选项 | 无 |
RadioActivity | 定义了RadioActivity的类 | 无 |
RegisterActivity | 定义了RegisterActivity的类 | 无 |
RegisterActivity.Reg | 用户注册 | String uid,String upass,Boolean man,int hobby |
SearchView | 定义了SearchView的类 | 无 |
SearchView. setSearchWay | 设置匹配数据的方法 | SearchWay search |
SearchView .getText | 获取搜索框的文字 | 无 |
SearchView .afterTextChanged | 搜索框的文字发生变化 | Editable s |
SearchView .setWaitTime | 设置搜索延时时间 | int waitTime |
SearchView .WaitThread | 延时搜索的线程 | 无 |
SearchWay | 定义用于匹配项的内部类 | 无 |
SongItemActivity | 定义了SongItemActivity的类 | 无 |
Songs | 定义了Songs的类 | 无 |
Songs.initSeekBar | 初始化进度条 | 无 |
Songs .updateProgress | 跟新音乐播放的进度条 | 无 |
Songs .MyOnClickListener | 切换歌曲详细信息内部类 | 无 |
StartActivity | 定义了StartActivity的类 | 无 |
ViewPagerAdapter | 定义了ViewPagerAdapter的内部类 | 无 |
8 总体设计
根据需求分析和系统运行的需要,PsyDuck Music系统需要实现六个主要模块的功能。
-
用户登录:任何一个系统,都需要一个访问系统的入口。用户需要通过自行注册帐号密码登陆PsyDuck Music App,验证通过后才能使用系统。
-
用户注册:主要包括四个信息。本系统用户需要输入用户名,密码,性别,爱好完成注册。用户一旦完成注册,就能够成为PsyDuck Music的会员登入App。
-
歌曲查询:用户需要在登录以后,才能进行歌曲。首先用户进入到“查找”的菜单栏下,输入需要查找的歌曲名称或者歌手名称即可。
-
歌曲播放:歌曲播放作为系统的核心模块。用户需要在登录以后,才能进行歌曲播放。首先用户进入到一个歌单的显示页面,点击“顺序播放”按钮即可。
-
动态发布:用户需要在登录以后,才能进行动态发布。首先用户进入到“动态”的菜单栏下,输入动态内容,点击“发布动态”按钮即可。
-
动态查询:用户需要在登录以后,才能进行动态查询。首先用户进入到“动态”的菜单栏下,PsyDuck Music会自动进行查询并显示用户的历史动态。
-
个人/关注者/粉丝信息查看:用户需要在登录以后,才能进行关注/粉丝查看。首先用户进入到“我的”的菜单栏下,点击对应的按钮,PsyDuck Music切换页面显示用户自己/关注者/粉丝的信息。
9 详细设计
9.1PsyDuck Music初始化页面
PsyDuck Music初始化页面,在程序开始运行时显示,3秒过后会自动跳转到登录界面。
9.2PsyDuck Music登录页面
9.3PsyDuck Music项目介绍页面
项目介绍界面,用户可以看到关于PsyDuck Music的项目介绍。
9.4 PsyDuck Music作者信息页面
9.5 PsyDuck Music用户注册页面
注册界面。用户需要自己设置自己的用户名、密码、性别、爱好等信息。完成注册后,自动跳到登录界面。
9.6 PsyDuck Music“我的”页面
“我的”界面。用户在登陆之后默认显示的页面,内含个人信息和歌单信息。
9.7 PsyDuck Music“电台”页面
“电台”界面,内含活动轮播图信息和电台信息。
9.8 PsyDuck Music歌曲查询页面
歌曲查询页面,用户登录以后,进入到“查找”的菜单栏下,输入需要查找的歌曲名称或者歌手名称可进行歌曲查询。
9.9 PsyDuck Music歌曲播放页面
歌曲播放页面,用户登录以后,进入到歌单的显示页面,点击“顺序播放”可进行歌曲播放。
9.10 PsyDuck Music动态发布页面
动态发布页面,用户登录以后,进入到“动态”的菜单栏下,输入动态内容并点击“发布动态”可进行动态发布。
9.11 PsyDuck Music动态查询页面
动态查询页面,用户登录以后,进入到“动态”的菜单栏下,PsyDuck Music会自动进行查询并显示用户的历史动态。
9.12 PsyDuck Music信息查看页面
信息查看界面可分为三类,一是用户自己的信息界面,二是用户关注者的信息界面,三是用户粉丝的信息界面,用户登录后,在“我的”菜单栏下点击相应的按钮即可查看对应信息界面。
10 工作总结
通过从开学到现在的移动开发课程学习,我收获了很多知识,得到了很大的提升。这将近大半个月的系统开发,更是让我的动手开发能力得到了进步。在PsyDuck Music的系统开发的过程中遇到了超多问题,很幸运的是都有人经历过,大多数通过百度的方式都可以解决。其中有一个错误,让人印象很深的是百度所给的解决方法无法解决,最后是通过阅读安卓源码的方式解决的(附安卓源码地址:https://www.androidos.net.cn/sourcecode)。我向来很少阅读英文文献,平时对这个还会有些抗拒,但是这次系统开发让我们意识到,其实英文API对于理解程序和应用有很大的帮助,有一种醍醐灌顶的感觉。另外,感谢课程老师的耐心指导,在系统开发的过程中,我体会过遇到问题的烦躁、问题没被解决的焦虑以及解决问题后的满足和自豪,这一切都是值得的。
(遇到问题汇总博客网址:
https://blog.csdn.net/qq_44702847/article/details/106708544)
同时,PsyDuck Music这个系统也存在很多不足,比如系统的权限安全问题,由于自己的水平不高,所有的代码内容都是在同一个包里的,这就带来了系统的安全问题。针对这一点,我后续会进行改进,并更新到GitHub网站上去,网址:https://github.com/ZUFEcsc/Android-PsyDuck-Music。
11 附录
11.1PsyDuckMusicActivity.java代码
package com.cn.csnb;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
public class PsyDuckMusicActivity extends Activity {
public static final String SP_INFO = "zscd_data";
public static final String USERID = "UserId";
public static final String USERPASS = "UserPass";
private EditText etUid;
private EditText etUpwd;
private String uidStr;
private String upwdStr;
private CheckBox cb;
private TextView tvPsyduck;
private TextView tvCsnb;
String sq;
Connection conn;
Statement st;
ResultSet rs;
final String driverName="com.microsoft.sqlserver.jdbc.SQLServerDriver";
final String dbURL="jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ZSCD";
final String userName="sa";
final String userPwd="123";
String sqlStr;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.psy_duck_music);
//跳转项目信息
tvPsyduck = (TextView)this.findViewById(R.id.tv_psyduck);
tvPsyduck.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
Intent itPsyduck = new Intent(PsyDuckMusicActivity.this, PsyDuckIntroduction.class);
startActivity(itPsyduck);
}
});
//跳转个人信息
tvCsnb =(TextView)this.findViewById(R.id.tv_writer);
tvCsnb.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
Intent itCsnb = new Intent(PsyDuckMusicActivity.this, Csnb.class);
startActivity(itCsnb);
}
});
etUid = (EditText)this.findViewById(R.id.etUid);
etUpwd = (EditText)this.findViewById(R.id.etUpwd);
cb = (CheckBox)this.findViewById(R.id.cbRemember);
checkIfRemember();
Button btnLogin = (Button) this.findViewById(R.id.button1);
btnLogin.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
String userName = etUid.getText().toString().trim();
String userPass = etUpwd.getText().toString().trim();
// SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase("/data/data/com.cn.csnb/zscd.db", null);
// Cursor qc = db.query("user", null, "uname=? and upwd=?", new String[] { userName, userPass }, null,
// null, null);
// if (qc.getCount() > 0) {
// Intent it = new Intent(ZscdcsActivity.this, FrameTabActivity.class);
// startActivity(it);
//// qc.close();
//// db.close();
// }
Intent it = new Intent(PsyDuckMusicActivity.this, FrameTabActivity.class);
startActivity(it);
}
});
Button btReg = (Button) this.findViewById(R.id.button2);
btReg.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
Intent it = new Intent(PsyDuckMusicActivity.this, RegisterActivity.class);
startActivity(it);
}
});
// Button btLogin = (Button)this.findViewById(R.id.button1);
// btLogin.setOnClickListener(new View.OnClickListener() {
// public void onClick(View v) {
// // TODO Auto-generated method stub
// Intent it = new Intent(ZscdcsActivity.this,FrameTabActivity.class);
// startActivity(it);
// }
// });
//
// Button bt = (Button)this.findViewById(R.id.button2);
// bt.setOnClickListener(new View.OnClickListener() {
//
// public void onClick(View v) {
// // TODO Auto-generated method stub
// Intent it = new Intent(ZscdcsActivity.this,RegisterActivity.class);
// startActivity(it);
// }
// });
Button btCreate = (Button)this.findViewById(R.id.button3);
btCreate.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase("/data/data/com.cn.csnb/zscd.db", null);
String sql = "create table user(uid integer primary key autoincrement,uname text,upwd text)";
db.execSQL(sql);
}
});
btCreate.setVisibility(View.GONE);
}
@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
if(cb.isChecked())
{
uidStr = etUid.getText().toString().trim();
upwdStr = etUpwd.getText().toString().trim();
rememberMe(uidStr,upwdStr);
}
}
//从SharedPreferences中读取用户的账号和密码
public void checkIfRemember()
{
SharedPreferences sp = getSharedPreferences(SP_INFO,MODE_PRIVATE);
uidStr = sp.getString(USERID, null);
upwdStr = sp.getString(USERPASS, null);
if(uidStr!=null && upwdStr!=null)
{
etUid.setText(uidStr);
etUpwd.setText(upwdStr);
cb.setChecked(true);
}
}
//将用户的id和密码存入SharedPreferences
public void rememberMe(String uid,String upwd)
{
SharedPreferences sp = getSharedPreferences(SP_INFO,MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString(USERID,uid);
editor.putString(USERPASS, upwd);
editor.commit();
}
}
11.2 RegisterActivity.java代码
package com.cn.csnb;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
public class RegisterActivity extends Activity {
private EditText etUserName;
private EditText etUserPass;
String sq;
Connection conn;
Statement st;
ResultSet rs;
final String driverName="com.microsoft.sqlserver.jdbc.SQLServerDriver";
final String dbURL="jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ZSCD";
final String userName="sa";
final String userPwd="123";
String sqlStr;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.register);
etUserName = (EditText)this.findViewById(R.id.editText1);
etUserPass = (EditText)this.findViewById(R.id.editText2);
Button btReg = (Button)this.findViewById(R.id.button1);
Button btBack = (Button)this.findViewById(R.id.button2);
btReg.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase("/data/data/com.cn.csnb/zscd.db", null);
String userName = etUserName.getText().toString().trim();
String userPass = etUserPass.getText().toString().trim();
String sql = "insert into user(uname,upwd) values('"+userName+"','"+userPass+"')";
db.execSQL(sql);
db.close();
try {
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
conn=DriverManager.getConnection(dbURL,userName,userPwd);
sqlStr="insert into users values('"+userName+"','"+userPass+"')";
st=conn.createStatement();
st.execute(sqlStr);
}
catch(Exception e1) {
System.out.print("数据库连接失败"+e1.getMessage());
}
finish();
if(userName != "" & userPass !="")
{
AlertDialog.Builder builder = new Builder(RegisterActivity.this);
builder.setTitle("恭喜") ;
builder.setMessage("用户 "+userName+" 注册成功 ~ " ) ;
builder.setPositiveButton("确定" ,null);
builder.show();
Intent itreturn = new Intent(RegisterActivity.this,PsyDuckMusicActivity.class);
startActivity(itreturn);
}
}
});
btBack.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
finish();
}
});
}
}
11.3 SearchView.java代码
package com.cn.csnb;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Handler;
import android.os.Message;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Administrator on 2016/8/31.
* 搜索框
*/
public class SearchView extends LinearLayout implements TextWatcher{
private EditText et_search; //输入框
private ImageView iv_clear; //删除图标
private SearchWay mSearch; //匹配方法
private String searchText; //改变后的文字
private WaitThread waitThread; //等待线程
private int waitTime = 200; //延时搜索时间,默认200ms
private int curTime; //当前延时时间
private Handler mHandler;
public SearchView(Context context, AttributeSet attrs) {
super(context, attrs);
//加载布局文件
View view = LayoutInflater.from(context).inflate(R.layout.find, null);
//获取控件
et_search = (EditText) view.findViewById(R.id.ed_find);
iv_clear = (ImageView) view.findViewById(R.id.img_clear);
//设置清空按钮的触发器
iv_clear.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
et_search.setText("");
}
});
//读取属性
TypedArray typed = context.obtainStyledAttributes(attrs, R.styleable.SearchView);
String s;
//文字大小
float textSize = typed.getDimension(R.styleable.SearchView_sv_textSize, 15);
et_search.setTextSize(textSize);
//搜索框文字
s = typed.getString(R.styleable.SearchView_sv_text);
if (s != null){
et_search.setText(s);
}
//提示文字
s = typed.getString(R.styleable.SearchView_sv_hint);
if (s != null){
et_search.setHint(s);
}
//是否隐藏搜索图标
// boolean hideImg = typed.getBoolean(R.styleable.SearchView_sv_hideImg, false);
// if (hideImg) {
// view.findViewById(R.id.img_search).setVisibility(GONE);
// }
//回收资源
typed.recycle();
//文字改变监听
et_search.addTextChangedListener(this);
//异步处理
mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
//更新回调接口
if (0 == msg.what){
waitThread = null;
//匹配结果回调
List searchList = new ArrayList();
List list = mSearch.getData();
if (list != null) {
for (Object o : list) {
if (mSearch.matchItem(o, searchText)) {
searchList.add(o);
}
}
mSearch.update(searchList);
}
}
}
};
//把布局添加到当前控件中
ViewGroup.LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
addView(view, params);
}
/**
* 设置匹配数据的方法
*/
public void setSearchWay(SearchWay search){
mSearch = search;
}
/**
* 获取搜索框的文字
*/
public String getText(){
return et_search.getText().toString();
}
public SearchWay getSearchWay() {
return mSearch;
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
public void afterTextChanged(Editable s) {
//删除图标
if (s.toString().isEmpty()){
iv_clear.setVisibility(GONE);
}else {
iv_clear.setVisibility(VISIBLE);
}
if (mSearch != null) {
if (null == waitThread) {
waitThread = new WaitThread();
waitThread.start();
} else {
//搜索框的文字发生变化就重置等待时间
if (!searchText.equals(s.toString())) {
curTime = 0;
}
}
}
searchText = s.toString();
}
/**
* 设置搜索延时时间
* @param waitTime 毫秒,精度为100ms
*/
public void setWaitTime(int waitTime){
this.waitTime = waitTime;
}
/**
* 延时搜索的线程
*/
private class WaitThread extends Thread{
@Override
public void run() {
//等待延时
for (curTime = 0; curTime < waitTime; curTime += 100){
try {
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
}
mHandler.sendEmptyMessage(0);
}
}
/**
* 用于匹配项
*/
public static abstract class SearchWay<T>{
/**
* @return 数据源
*/
public abstract List<T> getData();
/**
* @return item中是否包含有s
*/
public abstract boolean matchItem(T item, String s);
/**
* 更新列表
* @param resultList 匹配的数据,重新加载到列表
*/
public abstract void update(List<T> resultList);
}
}
下一篇: 2018今日头条内推笔试1