android开发可用技巧
都是本人在android开发的学习过程中用到的小技巧,记录于此,便于以后查找
获取屏幕尺寸
DisplayMetrics dm = getResources().getDisplayMetrics();
int screenWidth = dm.widthPixels;
int screenHeight = dm.heightPixels;
自定义view获取自身尺寸
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
float width = getMeasuredWidth();
float height = getMeasuredHeight();
}
dp和px转换
//dp转px
final float scale = context.getResources().getDisplayMetrics().density;
pxValue = (int) (dpValue * scale + 0.5f);
//px转dp
final float scale = context.getResources().getDisplayMetrics().density;
dpValue = (int) (pxValue / scale + 0.5f);
获取状态栏高度
public int getStatusBarHeight() {
Resources resources = Resources.getSystem();
int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
return resources.getDimensionPixelSize(resourceId);
}
设置状态栏字体颜色
//isLightModel为true字体黑,反之字体白
public static void setStatusBarLightMode(Activity activity, boolean isLightMode) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Window window = activity.getWindow();
int option = window.getDecorView().getSystemUiVisibility();
if (isLightMode) {
option |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
} else {
option &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
}
window.getDecorView().setSystemUiVisibility(option);
}
}
状态栏透明
//方法一:不兼容低版本
//res/values/styles.xml(改AppTheme的方式)
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowTranslucentStatus">false</item>
<item name="android:windowTranslucentNavigation">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
//方法二:兼容低版本
public void makeStatusBarTransparent(Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
return;
}
Window window = activity.getWindow();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
int option = window.getDecorView().getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
window.getDecorView().setSystemUiVisibility(option);
window.setStatusBarColor(Color.TRANSPARENT);
} else {
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
//再结合隐藏标题栏即可
按返回键
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
//业务逻辑
return true;
}
return super.onKeyDown(keyCode, event);
}
字体不跟随系统设置
@Override
public Resources getResources() {
Resources res=super.getResources();
Configuration config=new Configuration();
config.setToDefaults();
res.updateConfiguration(config,res.getDisplayMetrics());
return res;
}
去除标题栏
ActionBar actionBar = getSupportActionBar();
if(actionBar != null){
actionBar.hide();
}
广播监听网络状态
//需要的uses-permission:
android.permission.INTERNET
android.permission.ACCESS_NETWORK_STATE
//onResume中:
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(netStateReceiver,filter);
//定义广播接收器
BroadcastReceiver netStateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(action.equals(ConnectivityManager.CONNECTIVITY_ACTION)){
//此时网络状态发生变化->调用获取网络状态的方法获取网络状态即可
}
}
};
获取当前网络状态
//需要的uses-permission:
android.permission.INTERNET
android.permission.ACCESS_NETWORK_STATE
int getNetWorkStatus(Context context){
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
if (activeNetworkInfo!=null&&activeNetworkInfo.isConnected()){
if (activeNetworkInfo.getType()==(ConnectivityManager.TYPE_WIFI)){
return NETWORK_WIFI;
}else if (activeNetworkInfo.getType()==(ConnectivityManager.TYPE_MOBILE)){
return NETWORK_MOBILE;
}
}else {
return NETWORK_NONE;
}
return NETWORK_NONE;
}
收起软键盘
private void hideKeyboard(){
View view = getCurrentFocus();
if(view != null){
InputMethodManager inputMethodManager = (InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}
}
数据存储到手机本地
SharedPreferences.Editor editor = getSharedPreferences("文件名",MODE_PRIVATE).edit();
editor.putString("key",value);
editor.apply();
读取手机本地存储的数据
SharedPreferences pref = getSharedPreferences("文件名",MODE_PRIVATE);
String username = pref.getString("key","默认值");
跳转至浏览器打开网站
Uri uri = Uri.parse("http://www.xxx.cn/");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
正则判断手机号格式是否正确
private boolean isPhone(String phone) {
String regex = "^((13[0-9])|(14[5,7,9])|(15([0-3]|[5-9]))|(166)|(17[0,1,3,5,6,7,8])|(18[0-9])|(19[8|9]))\\d{8}$";
if (phone.length() != 11) {
return false;
} else {
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(phone);
return m.matches();
}
}
长按控件
控件.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
//单指按下
break;
case MotionEvent.ACTION_UP:
//单指释放
break;
case MotionEvent.ACTION_MOVE:
//移动
break;
case MotionEvent.ACTION_CANCEL:
//取消
break;
case MotionEvent.ACTION_POINTER_DOWN:
//多指按下
break;
case MotionEvent.ACTION_POINTER_UP:
//多指释放
break;
}
return false;
}
});
更新app
//动态注册广播接收器
private void initReceiver(){
IntentFilter filter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
registerReceiver(downloadApkReceiver,filter);
}
//下载更新包
private void downloadApk(){
DownloadManager.Request request = new DownloadManager.Request(Uri.parse("下载地址"));
request.setDestinationInExternalFilesDir(this,Environment.DIRECTORY_DOWNLOADS,"xxx.apk");
request.setTitle("xxx.apk");
request.setDescription("xxx");
request.setVisibleInDownloadsUi(true);
DownloadManager apkDownloadManager = (DownloadManager)getSystemService(DOWNLOAD_SERVICE);
long mReference = apkDownloadManager.enqueue(request);
}
//定义下载完成时的广播接收器
BroadcastReceiver downloadApkReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(action.equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)){
//下载完成,提交handler处理
}
}
};
//handler中
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
boolean canInstallApk = getPackageManager().canRequestPackageInstalls();
if(!canInstallApk){
//向用户拿安装apk的权限
Uri uri = Uri.parse("package:"+getPackageName());
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,uri);
startActivityForResult(intent,996);
}else{
installApk("storage/emulated/0/Android/data/com.example.xxx/files/Download/xxx.apk");
}
}else{
installApk("storage/emulated/0/Android/data/com.example.xxx/files/Download/xxx.apk");
}
//拿到权限后安装
@Override
protected void onActivityResult(int requestCode,int resultCode,Intent data){
super.onActivityResult(requestCode,resultCode,data);
if(requestCode == 996){
installApk("storage/emulated/0/Android/data/com.example.xxx/files/Download/xxx.apk");
}
}
//安装更新包
private void installApk(String downloadApk) {
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
builder.detectFileUriExposure();
Uri uri = Uri.fromFile(new File(downloadApk));
Intent installIntent = new Intent(Intent.ACTION_VIEW);
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
installIntent.setDataAndType(uri, "application/vnd.android.package-archive");
startActivity(installIntent);
}
界面不被软键盘挤上去
//activity配置xml中
android:windowSoftInputMode:adjustPan
获取系统当前时间(自定义时间格式)
SimpleDateFormat timeFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
Date curDate = new Date(System.currentTimeMillis());
String nowTime = timeFormat.format(curDate);
设置透明
//方法一
xml中 android:alpha 设置0到1
//方法二
xml中 android:background 设置#xx红绿蓝 xx为16进制
//方法三
view.getBackground().setAlpha(x); 设置0到255
监听软键盘点击完成
xml中 EditText
android:singleLine true
android:imeOptions="某action"
java中
EditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) {
if(i == EditorInfo.IME_ACTION_SEARCH){
//业务逻辑
return true;
}
return false;
}
});
imeOptions可选值分别是:
actionDone 完成 EditorInfo.IME_ACTION_DONE
actionGo 前进 EditorInfo.IME_ACTION_GO
actionPrevious 上一项 EditorInfo.IME_ACTION_PREVIOUS
actionNext 下一项 EditorInfo.IME_ACTION_NEXT
actionSearch 搜索 EditorInfo.IME_ACTION_SEARCH
actionSend 发送 EditorInfo.IME_ACTION_SEND
actionUnspecified 未指定 EditorInfo.IME_ACTION_UNSPECIFIED
actionNone 无动作 EditorInfo.IME_ACTION_NONE
延时处理延迟处理
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//业务逻辑
}
},延迟的时间毫秒);
数据库SQLite
//先创建一个数据库类
public class DBHelper extends SQLiteOpenHelper {
private static Integer Version = 1;
public DBHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version){
super(context, name, factory, version);
}
public DBHelper(Context context, String name, int version){
this(context,name,null,version);
}
public DBHelper(Context context,String name){
this(context, name, Version);
}
@Override
public void onCreate(SQLiteDatabase db) {
String sql = "create table record(id integer primary key autoincrement,content text,time varchar(60))";
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
//java中使用
DBHelper dbHelper = new DBHelper(context,"record");
SQLiteDatabase db = dbHelper.getWritableDatabase();
//增
ContentValues values = new ContentValues();
values.put("content",content);
values.put("time",time);
db.insert("record",null,values);
//删
db.delete("record","id=?",new String[]{
id});
//改
ContentValues values = new ContentValues();
values.put("content",content);
values.put("time",time;
db.update("record",values,"id=?",new String[]{
id});
//查
Cursor cursor = db.query("record", new String[]{
"id","content","time"}, "content LIKE ? ",new String[]{
"%"+keywords+"%"},null,null,"time DESC");
while (cursor.moveToNext()) {
String id = cursor.getString(cursor.getColumnIndex("id"));
String content = cursor.getString(cursor.getColumnIndex("content"));
String time = cursor.getString(cursor.getColumnIndex("time"));
//业务逻辑
}
//关闭数据库
db.close();
shape
corners //圆角属性
android:radius //数值
android:topLeftRadius //数值
android:topRightRadius //数值
android:bottomLeftRadius //数值
android:bottomRightRadius //数值
gradient //渐变属性
android:angle //渐变角度,必须为45的倍数,0为从左到右,90为从上到下
android:startColor //渐变开始点的颜色
android:endColor //渐变结束点的颜色
android:centerColor //渐变中间点的颜色
android:centerX //渐变中心X的相当位置,范围为0~1
android:centerY //渐变中心Y的相当位置,范围为0~1
android:type=["linear" | "radial" | "sweep"] //渐变类型,线性渐变(默认)/放射渐变/扫描式渐变
android:gradientRadius= //渐变的半径,只有当渐变类型为radial时才能使用
android:useLevel=["true" | "false"] //使用LevelListDrawable时就要设置为true。设为false时才有渐变效果
padding //边距属性
android:left //数值
android:top //数值
android:right //数值
android:bottom //数值
solid //填充属性
android:color //颜色
stroke //描边属性
android:width //宽度
android:color //颜色
android:dashWidth //虚线的宽度,值为0时是实线
android:dashGap //虚线的间隔
size //大小属性
android:width //数值
android:height //数值
selector
//注:用于设置background时用android:drawable,用于设置各种color时用android:color
item android:state_pressed="false"
android:drawable(或color)//未被按住时的样子
item android:state_pressed="true"
android:drawable(或color) //被按住时的样子
类似的还有:
//设置是否选中状态,true表示已选中,false表示未选中
android:state_selected
//设置是否勾选状态,主要用于CheckBox和RadioButton,true表示已被勾选,false表示未被勾选
android:state_checked
//设置勾选是否可用状态,类似state_enabled,只是state_enabled会影响触摸或点击事件,state_checkable影响勾选事件
android:state_checkable
//设置是否获得焦点状态,true表示获得焦点,默认为false,表示未获得焦点
android:state_focused
//设置触摸或点击事件是否可用状态,一般只在false时设置该属性,表示不可用状态
android:state_enabled
message带数据提交handler
//message:
Message message = new Message();
message.what = 1;
Bundle bundle = new Bundle();
bundle.putString("key",value);
message.setData(bundle);
handler.sendMessage(message);
//handler中:
String value = message.getData().getString("key");
数据转json
//简单的键值对数组
JSONObject jsonObject = new JSONObject();
jsonObject.put("key1","value1");
jsonObject.put("key2","value2");
String json = jsonObject.toString();
//稍复杂的多维数组,子元素为自定义类,且子元素没键名
需要引入gson:implementation 'com.google.code.gson:gson:2.7'
List<自定义类> list = new ArrayList<>();
list.add(new 自定义类(xxx));
list.add(new 自定义类(xxx));
Gson gson = new Gson();
String json = gson.toJson(list);
//更复杂的多维数组,子元素为自定义类,且子元素有键名
需要引入gson:implementation 'com.google.code.gson:gson:2.7'
Map map = new HashMap<>();
map.put("key1",new 自定义类(xxx));
map.put("key2",new 自定义类(xxx));
String json = gson.toJson(map);
解析json
//JSONObject方式解析(灵活方便,能满足绝大多数情况)
try{
JSONObject jsonObject = new JSONObject("json字符串");
String code = jsonObject.getString("code");
JSONObject result = jsonObject.getJSONObject("result");
JSONArray data = result.getJSONArray("data");
for (int i = 0;i < data.length();i++){
JSONObject value = data.getJSONObject(i);
String title = value.getString("title");
}
}catch(Exception e){
e.printStackTrace();
}
//gson方式解析(需要自定义类,麻烦,适用情况:自定义类存在)
需要引入gson:implementation 'com.google.code.gson:gson:2.7'
Gson gson = new Gson();
List<自定义类> list = new ArrayList<>();
list = gson.fromJson("json字符串",new TypeToken>(){
}.getType());
bitmap转base64
public String bitmapToBase64(Bitmap bitmap){
String result = null;
ByteArrayOutputStream byteArrayOutputStream = null;
try{
if(bitmap != null){
byteArrayOutputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream);
byteArrayOutputStream.flush();
byteArrayOutputStream.close();
byte[] bitmapBytes = byteArrayOutputStream.toByteArray();
result = Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
}
}catch(IOException e){
e.printStackTrace();
}finally{
try{
if(byteArrayOutputStream != null){
byteArrayOutputStream.flush();
byteArrayOutputStream.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
return result;
}
图片处理(缩放尺寸、改变质量、旋转)
//缩放图片大小
Matrix matrix = new Matrix();
matrix.setScale(0.1f, 0.1f);
Bitmap saveBitmap = Bitmap.createBitmap(originBitmap,0,0,originBitmap.getWidth(),originBitmap.getHeight(),matrix,true);
//降低图片质量
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int quality = 50;//图片质量,0-100,值越小质量越差
originBitmap.compress(Bitmap.CompressFormat.JPEG,quality,byteArrayOutputStream);
Bitmap saveBitmap = BitmapFactory.decodeStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()),null,null);
//旋转图片
int width = originBitmap.getWidth();
int height = originBitmap.getHeight();
Matrix matrix = new Matrix();
matrix.setRotate(angle);//angle为旋转的角度
Bitmap saveBitmap = Bitmap.createBitmap(originBitmap, 0, 0, width, height, matrix, false);
okhttp3发送get请求
//需要引入okhttp3:implementation 'com.squareup.okhttp3:okhttp:3.4.1'
Thread thread = new Thread(){
public void run(){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
try{
Response response = client.newCall(request).execute();
String result = response.body().string();
//业务逻辑(若要更新UI需提交给handler处理)
}catch(Exception e){
e.printStackTrace();
}
}
};
thread.start();
okhttp3发送post请求
//需要引入okhttp3:implementation 'com.squareup.okhttp3:okhttp:3.4.1'
Thread thread = new Thread(){
public void run(){
OkHttpClient client = new OkHttpClient();
RequestBody requestBody = new FormBody.Builder().add("key",value).build();
Request request = new Request.Builder().url(url).post(requestBody).build();
try{
Response response = client.newCall(request).execute();
String result = response.body().string();
//业务逻辑(若要更新UI需提交给handler处理)
}catch(Exception e){
e.printStackTrace();
}
}
};
thread.start();
okhttp3发送json格式的post请求
//需要引入okhttp3:implementation 'com.squareup.okhttp3:okhttp:3.4.1'
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
Thread thread = new Thread(){
@Override
public void run(){
String json = "自己组织json";
OkHttpClient okHttpClient = new OkHttpClient();
RequestBody requestBody = RequestBody.create(JSON,json);
Request request = new Request.Builder().url(url).post(requestBody).build();
try{
Response response=okHttpClient.newCall(request).execute();
String result = response.body().string();
//业务逻辑(若要更新UI需提交给handler处理)
}catch(Exception e){
e.printStackTrace();
}
}
};
thread.start();
动态设置文字样式
设置字体大小:
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX,getResources().getDimensionPixelSize(R.dimen.xxx));
//如果设置后字体过大,用:
textView.setTextSize(android.util.TypedValue.COMPLEX_UNIT_PX,getResources().getDimension(R.dimen.xx));
设置字体颜色:
textView.setTextColor(getResources().getColor(R.color.xxx));
设置样式:
textView.setTypeface(Typeface.SANS_SERIF,Typeface.NORMAL);
Typeface.BOLD //粗体
Typeface.BOLD_ITALIC //粗斜体
Typeface.ITALIC //斜体
Typeface.NORMAL //常规
时间选择器
private TimePickerView pvTime;
//需要显示时间的控件点击显示选择器
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
pvTime.show(textView);
}
});
//设置选择器
Calendar selectedDate = Calendar.getInstance();//默认选中时间
Calendar startDate = Calendar.getInstance();
startDate.set(2018, 12, 1);//可选时间范围的初始时间
Calendar endDate = Calendar.getInstance();
endDate.set(2029, 12, 31);//可选时间范围的结束时间
pvTime = new TimePickerView.Builder(this, new TimePickerView.OnTimeSelectListener() {
@Override
public void onTimeSelect(Date date, View v) {
TextView btn = (TextView) v;
btn.setText(getTimes(date));//getTimes为自定义函数,目的是把date转成需要的时间格式
}
}).setType(new boolean[]{
true, true, true, false, false, false})//显示选项年月日时分秒
.setLabel("年", "月", "日", "时", "", "")//显示格式
.isCenterLabel(true)
.setDividerColor(Color.DKGRAY)
.setContentSize(21)
.setDate(selectedDate)
.setRangDate(startDate, endDate)
.setDecorView(null)
.build();
时间选择器-年月日
Calendar c = Calendar.getInstance();
new DatePickerDialog(this, new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker datePicker, int i, int i1, int i2) {
String date = i + "年" + (i1+1) + "月" + i2 + "日";
}
},c.get(Calendar.YEAR),c.get(Calendar.MONTH),c.get(Calendar.DAY_OF_MONTH)).show();
时间选择器-时分
Calendar c = Calendar.getInstance();
new TimePickerDialog(this, new TimePickerDialog.OnTimeSetListener() {
@Override
public void onTimeSet(TimePicker timePicker, int i, int i1) {
String time = i + "点" + i1 + "分";
}
},c.get(Calendar.HOUR_OF_DAY),c.get(Calendar.MINUTE),true).show();
保留两位小数
private String keep2decimal(float f){
DecimalFormat df = new DecimalFormat("#.00");
return df.format(f);
}
ListView(子元素为一条文本)
活动布局xml中加控件:ListView
String[] data = {
"","","","",""};
ArrayAdapter adapter = new ArrayAdapter(XXActivity.this,android.R.layout.simple_list_item_1,data);
ListView list = (ListView)findViewById(R.id.list);
list.setAdapter(adapter);
//点击获取子元素的值
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView parent, View view, int position, long id) {
String string = ((TextView)view).getText().toString();
}
});
recyclerView
需要引入:implementation 'com.android.support:recyclerview-v7:28.0.0'
活动布局xml中加控件:RecyclerView
布局xml中加子元素布局;
新建adapter:
public class XXAdapter extends RecyclerView.Adapter{
private List<XX> list;
private Context context;
public XXAdapter(List<XX> list, Context context){
this.list = list;
this.context = context;
}
@Override
public int getItemCount(){
//根据获取到的list显示希望显示的子元素个数
return list.size();
}
@Override
public int getItemViewType(int position){
//position为list的角标,根据业务逻辑返回不同的值
return 1;
}
class AHolder extends RecyclerView.ViewHolder{
private View aView;
public AHolder(View itemView){
super(itemView);
aView = itemView;
}
}
class BHolder extends RecyclerView.ViewHolder{
private TextView bTV;
public BHolder(View itemView){
super(itemView);
bTV = (TextView)itemView.findViewById(R.id.b);
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
//根据getItemViewType返回的值返回不同的holder
if(viewType == 1){
return new AHolder(LayoutInflater.from(context).inflate(R.layout.a_item,null));
}else{
return new BHolder(LayoutInflater.from(context).inflate(R.layout.b_item,null));
}
}
//点击子元素监听的接口
public interface OnItemClickListener {
void onClick(XX xx);
}
private OnItemClickListener listener;
public void setOnItemClickListener(OnItemClickListener listener) {
this.listener = listener;
}
@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder,final int position){
if(holder instanceof AHolder){
((AHolder) holder).aView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) {
listener.onClick(list.get(position));
}
}
});
}else{
XX xx = list.get(position);
((BHolder) holder).bTV.setText(xx.getB());
}
}
}
activity中:
private RecyclerView recyclerView;
private GridLayoutManager mLayoutManager;
private XXAdapter adapter;
recyclerView = (RecyclerView)findViewById(R.id.recyclerView);
adapter = new XXAdapter(list,MainActivity.this);
mLayoutManager = new GridLayoutManager(MainActivity.this,1);//数字表示显示的列数
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setAdapter(adapter);
recyclerView.setItemAnimator(new DefaultItemAnimator());//默认动画样式,可自定义
//子项的点击事件处理
adapter.setOnItemClickListener(new XXAdapter.OnItemClickListener() {
@Override
public void onClick(XX xx) {
}
});
ImageView加载网络图片
需要引入:implementation 'com.github.bumptech.glide:glide:4.0.0'
布局xml中加入ImageView
Glide.with(context).load(url).into(imageView);
加载gif
需要引入:implementation 'com.github.bumptech.glide:glide:4.0.0'
布局xml中加入ImageView
Glide.with(context).load(url).asGif().diskCacheStrategy(DiskCacheStrategy.SOURCE).into(imageView);
//加上diskCacheStrategy的目的是为了加载更快
DiskCacheStrategy.NONE 什么都不缓存
DiskCacheStrategy.SOURCE 仅仅只缓存原来的全分辨率的图像
DiskCacheStrategy.RESULT 仅仅缓存最终的图像,即降低分辨率后的(或者是转换后的)
DiskCacheStrategy.ALL 缓存所有版本的图像(默认行为)
自定义控件组合
创建布局文件my_layout.xml,比如最外侧控件为LinearLayout,含一个TextView
创建java文件:
public class MyLayout extends LinearLayout {
public MyLayout(Context context,AttributeSet attributeSet){
super(context,attributeSet);
LayoutInflater.from(context).inflate(R.layout.my_layout,this);
//findViewById获取控件,并可添加事件监听
}
}
xml中:直接使用MyLayout控件
java中:layout.addView(myLayout);
加载摄像头并拍照
/需要权限:android.permission.CAMERA
布局xml中:TextureView
activity中:
需要:implements TextureView.SurfaceTextureListener
定义:
private TextureView cameraView;
private Camera mCamera;
业务逻辑:
cameraView = (TextureView)findViewById(R.id.cameraView);
cameraView.setSurfaceTextureListener(MainActivity.this);
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
// 打开相机 0后置 1前置
mCamera = Camera.open(0);
if (mCamera != null) {
try {
mCamera.setDisplayOrientation(90);// 设置预览角度,并不改变获取到的原始数据方向
// 绑定相机和预览的View
mCamera.setPreviewTexture(surface);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
mCamera.stopPreview();
mCamera.setPreviewCallback(null);
mCamera.release();
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
//某行为触发开启摄像头预览
mCamera.startPreview();
mCamera.autoFocus(null);
//某行为触发拍照(拍单张)
mCamera.takePicture(null,null,new Camera.PictureCallback(){
@Override
public void onPictureTaken(byte[] data,Camera camera){
Bitmap bitmap = BitmapFactory.decodeByteArray(data,0,data.length);
//业务逻辑(图片可能角度不对,需要旋转)
releaseCamera();
}
});
//某行为触发拍照(拍多张)
mCamera.setPreviewCallback(new Camera.PreviewCallback(){
@Override
public void onPreviewFrame(byte[] data,Camera camera){
//以下可以用和上面单张一样的处理方式,也可以用下面的处理方式
Camera.Size size = camera.getParameters().getPreviewSize();
try{
YuvImage image = new YuvImage(data,ImageFormat.NV21,size.width,size.height,null);
if(image!=null){
ByteArrayOutputStream stream = new ByteArrayOutputStream();
image.compressToJpeg(new Rect(0,0,size.width,size.height),80,stream);
Bitmap bmp = BitmapFactory.decodeByteArray(stream.toByteArray(),0,stream.size());
//业务逻辑
stream.close();
}
}catch(Exception e){
e.printStackTrace();
}
}
});
dialog:两个按钮
AlertDialog.Builder builder = new AlertDialog.Builder(this).setIcon(R.mipmap.图标).setTitle("标题").setMessage("内容")
.setPositiveButton("确定(积极)", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
//
}
}).setNegativeButton("取消(消极)", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
//
}
});
builder.create().show();
dialog:三个按钮
AlertDialog.Builder builder = new AlertDialog.Builder(this).setIcon(R.mipmap.图标).setTitle("标题").setMessage("内容")
.setPositiveButton("确定(积极)", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
//
}
}).setNeutralButton("不选(中立)", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
//
}
}).setNegativeButton("取消(消极)", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
//
}
});
builder.create().show();
dialog:列表
final String[] items = {
"item 1", "item 2", "item 3", "item 4", "item 5", "item 6"};
AlertDialog.Builder builder = new AlertDialog.Builder(this).setIcon(R.mipmap.图标)
.setTitle("标题")
.setItems(items, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
//(i为当前点击的项所在items脚标)
}
});
builder.create().show();
dialog:多选
final String[] items = {
"多选1", "多选2", "多选3", "多选4", "多选5", "多选6"};
boolean[] isSelect = {
false, false, false, false, false, false};//默认选中状态
AlertDialog.Builder builder = new AlertDialog.Builder(this).setIcon(R.mipmap.图标)
.setTitle("标题")
.setMultiChoiceItems(items, isSelect, new DialogInterface.OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i, boolean b) {
//(i为当前点击的项所在items脚标,b为点击后是否为选中状态)
}
}).setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
//选择时用数组记录选中的项,这里确定时循环数组处理业务
}
});
builder.create().show();
dialog:单选
final String[] items = {
"单选1", "单选2", "单选3", "单选4", "单选5", "单选6"};
AlertDialog.Builder builder = new AlertDialog.Builder(this).setIcon(R.mipmap.图标).setTitle("标题")
.setSingleChoiceItems(items, 默认选中脚标, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
//(i为当前点击的项所在items脚标)
}
}).setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
//
}
});
builder.create().show();
dialog:圆圈加载
ProgressDialog progressDialog = new ProgressDialog(this);
progressDialog.setIcon(R.mipmap.图标);
progressDialog.setTitle("标题");
progressDialog.setMessage("内容...");
//是否形成一个加载动画,true表示不明确加载进度形成转圈动画,false表示明确
progressDialog.setIndeterminate(true);
//点击返回键或者dialog四周是否关闭dialog
progressDialog.setCancelable(false);
progressDialog.show();
//加载完成后关闭dialog
progressDialog.dismiss();
dialog:进度条加载
final int MAX_VALUE = 100;
ProgressDialog progressDialog = new ProgressDialog(this);
progressDialog.setIcon(R.mipmap.图标);
progressDialog.setProgress(0);
progressDialog.setTitle("标题");
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMax(MAX_VALUE);
progressDialog.show();
new Thread(new Runnable() {
@Override
public void run() {
int progress = 0;
while (progress < MAX_VALUE) {
try {
Thread.sleep(100);
progress++;
progressDialog.setProgress(progress);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//加载完毕自动关闭dialog
progressDialog.cancel();
}
}).start();
dialog:自定义
第一种:纯自定义
View dialogView = View.inflate(this, R.layout.自定义布局, null);
dialogView.findViewById(R.id.xx).控件处理
AlertDialog.Builder builder = new AlertDialog.Builder(this).setView(dialogView);
AlertDialog alertDialog = builder.create();
alertDialog.show();
//完成后关闭dialog
alertDialog.dismiss();
第二种:只有中间部分是自定义,仍有标题按钮等
View dialogView = LayoutInflater.from(this).inflate(R.layout.自定义布局, null);
xx = dialogView.findViewById(R.id.xx);
AlertDialog.Builder builder = new AlertDialog.Builder(this).setView(dialogView)
.setIcon(R.mipmap.图标).setTitle("标题")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
//根据xx控件的值处理
}
});
builder.create().show();
第三种:view是java创建的控件
final EditText editText = new EditText(this);
其他和第二种一样,只是把editText作为setView()的参数
单选框
xml中:
一个RadioGroup下放入多个RadioButton
activity中:
设置默认选中项:
radioButton.setChecked(true);
选中事件监听:
radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup radioGroup, int i) {
RadioButton radioButton = (RadioButton)findViewById(radioGroup.getCheckedRadioButtonId());
String type = radioButton.getText().toString();
//
}
});
多选框
xml中:
多个CheckBox
activity中:
设置默认选中项:
checkBox.setChecked(true);
选中事件监听:
activity implements CompoundButton.OnCheckedChangeListener
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
//(b为点击后是否为选中状态)
//(compoundButton为选中的CheckBox,可用getText().toString()获取值)
}
下拉框
xml中:
一个Spinner
activity中:
Spinner spinner = (Spinner) findViewById(R.id.spinner);
final String[] items = new String[]{
"item1", "item2", "item3", "item4", "item5"};
ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item, items);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
spinner.setSelection(2);//默认选中项脚标
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView adapterView, View view, int i, long l) {
//(i为当前点击的项所在items脚标)
//view可以转换为TextView,然后设置字体样式
}
@Override
public void onNothingSelected(AdapterView adapterView) {
}
});
canvas
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
Paint.Style.STROKE:只画边框
Paint.Style.FILL:只填充内部
Paint.Style.FILL_AND_STROKE:既画边框又填充内部
paint.setAntiAlias(true);//开启抗锯齿
paint.setColor(Color.parseColor("#FF83FA"));
paint.setStrokeWidth(20);
//设置能画渐变背景的paint
SweepGradient mSweepGradient = new SweepGradient(
渐变中心x,
渐变中心y,
new int[]{
颜色、颜色。。。},
null);
paint.setShader(mSweepGradient);
//画点
canvas.drawPoint(x, y, paint);
//画线
canvas.drawLine(startX, startY, stopX, stopY, paint);
//画矩形
canvas.drawRect(left, top, right, bottom, paint);
//画圆角矩形
canvas.drawRoundRect(left, top, right, bottom, paint, x轴半径, y轴半径, paint);
//画椭圆
canvas.drawOval(left, top, right, bottom, paint);
//画圆
canvas.drawCircle(圆心x, 圆心y, 半径, paint);
//画圆弧
canvas.drawArc(left, top, right, bottom, startAngle, sweepAngle, useCenter, paint);
startAngle:圆弧开始的角度
sweepAngle:圆弧扫描的角度
useCenter:是否将中心点与圆弧连起来形成扇形
//画图片
canvas.drawBitmap(bitmap, left, top, paint);
//画文字
paint.setTextSize(100);
canvas.drawText("文字",文字左侧离左边缘距离,文字下面离上边缘距离,paint);
//如果需要文字居中
paint.setTextAlign(Paint.Align.CENTER);
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
float distance = (fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;
canvas.drawText(currentStageName,父级中心x,父级中心y+distance,paint);
自定义view
//values下创建attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyView">
<attr name="text" format="string" />
<attr name="size" format="integer" />
</declare-styleable>
</resources>
//MyView类
public class MyView extends View {
public MyView(Context context, AttributeSet attrs){
super(context, attrs);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyView);
String text = ta.getString(R.styleable.MyView_text);
int size = ta.getInteger(R.styleable.MyView_size, 100);
ta.recycle();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//canvas.drawC。。。
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//尺寸相关操作
}
}
//布局中放入MyView
app:text="测试text属性"
app:size="200"
从手机相册获取图片
需要权限:android:name="android.permission.READ_EXTERNAL_STORAGE"
打开相册:
Intent intent = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, RESULT_LOAD_IMAGE);
获取图片:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RESULT_LOAD_IMAGE && resultCode == RESULT_OK && null != data) {
Uri selectedImage = data.getData();
String[] filePathColumn = {
MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(selectedImage,filePathColumn,null,null,null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
String picturePath = cursor.getString(columnIndex);
Bitmap bitmap = getBitmapFromLocalImage(picturePath);
//
cursor.close();
}
}
从图片地址获取bitmap:
private Bitmap getBitmapFromLocalImage(String path){
Bitmap bitmap = null;
try {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(path));
bitmap = BitmapFactory.decodeStream(bis);
bis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
动态获取权限
private void requestPermission(){
String permissions[] = {
Manifest.permission.READ_EXTERNAL_STORAGE,
//...
};
ArrayList toApplyList = new ArrayList();
for(String perm : permissions){
if(PERMISSION_GRANTED != ContextCompat.checkSelfPermission(this,perm)){
toApplyList.add(perm);
}
}
String tmpList[] = new String[toApplyList.size()];
if(!toApplyList.isEmpty()){
ActivityCompat.requestPermissions(this,toApplyList.toArray(tmpList),123);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode,permissions,grantResults);
if (requestCode == 123) {
for (int i = 0; i < permissions.length; i++) {
if (grantResults[i] == PERMISSION_GRANTED) {
Toast.makeText(this, "" + "权限" + permissions[i] + "申请成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "" + "权限" + permissions[i] + "申请失败", Toast.LENGTH_SHORT).show();
}
}
}
}
TextView加载html代码
import static android.text.Html.FROM_HTML_MODE_LEGACY;
textView.setText(Html.fromHtml("HTML代码",FROM_HTML_MODE_LEGACY));
动态改变控件尺寸
ViewGroup.LayoutParams lp = view.getLayoutParams();
lp.width = 宽度;
lp.height = 高度;
view.setLayoutParams(lp);
减数定时器
private CountDownTimer cdt = new CountDownTimer(总执行时间, 每次执行时间间隔) {
@Override
public void onTick(long millisUntilFinished) {
//业务逻辑
}
@Override
public void onFinish() {
cdt.cancel();
}
};
cdt.start();
定时器
Timer timer = new Timer();
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
//业务逻辑
}
};
timer.schedule(timerTask,延迟的毫秒数,定时执行的毫秒数);
if(timer != null){
timer.cancel();
}
ImageView旋转
静态旋转:
xml设置rotation="度数"
动态旋转:
imageView.setPivotX(imageView.getWidth()/2);
imageView.setPivotY(imageView.getHeight()/2);//支点在图片中心
imageView.setRotation("度数");
EditText内容改变监听
editText.addTextChangedListener(new TextWatcher() {
@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) {
//业务逻辑
//将光标移动到末尾
//editText.setSelection(editText.getText().length());
}
});
EditText焦点变化监听
editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
//业务逻辑(hasFocus表示是否获得焦点)
}
});
循环父布局中的子元素
for (int i = 0;i < linearLayout.getChildCount(); i++){
if (linearLayout.getChildAt(i) instanceof TextView) {
TextView textView = (TextView)linearLayout.getChildAt(i);
//业务逻辑
}
}
彩色阴影
需要引入:implementation 'com.lijiankun24:shadowlayout:1.0.0'
布局xml中:
xmlns:app="http://schemas.android.com/apk/res-auto"
com.lijiankun24.shadowlayout.ShadowLayout
属性:
app:shadowColor="#66000000" 控制阴影的颜色,注意:颜色必须带有透明度的值
app:shadowDx="0dp" 控制阴影 x 轴的偏移量
app:shadowDy="3dp" 控制阴影 y 轴的偏移量
app:shadowRadius="10dp" 控制阴影的范围
app:shadowSide="all|left|right|top|bottom" 控制阴影显示的边界,共有五个值
setText设置拼接字符串
values-strings.xml中:
例:text:字符串%1$s整数%2$d小数%3$f
%n$s--->n表示目前是第几个参数 (比如%1$s中的1代表第一个参数),s代表字符串
%n$d--->n表示目前是第几个参数 (比如%2$d中的2代表第二个参数),d代表整数
%n$f--->n表示目前是第几个参数 (比如%3$f中的3代表第三个参数),f代表浮点数
textView.setText(String.format(getResources().getString(R.string.text),String,int,float));
ScrollView滑到底部或顶部
scrollView.post(new Runnable() {
@Override
public void run() {
//滑到底部
scrollView.fullScroll(View.FOCUS_DOWN);
//滑到顶部
scrollView.fullScroll(View.FOCUS_UP);
}
});
手机振动震动
权限:uses-permission android:name="android.permission.VIBRATE"
初始化:Vibrator vibrator = (Vibrator) getSystemService(Service.VIBRATOR_SERVICE);
判断是否有振动器:vibrator.hasVibrator()
短振动:vibrator.vibrate(1000); // 振动1秒
节奏振动:vibrator.vibrate(new long[]{
500, 1000, 500, 2000}, -1);
// 暂停500毫秒,振动1秒,暂停500毫秒,振动2秒
// 第二个参数为-1则振动一遍,第二个参数为0则重复振动
暂停振动:vibrator.cancel();
屏幕保持常亮
方法一:直接在xml顶层布局中:
android:keepScreenOn="true"
方法二:java中设置:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
从其它线程访问主线程更新UI
方法一:
Activity.runOnUiThread(new Runnable() {
@Override
public void run() {
//更新UI
}
});
方法二:
View.post(new Runnable() {
@Override
public void run() {
//更新UI
}
});
方法三:
view.postDelayed(new Runnable() {
@Override
public void run() {
//更新UI
}
}, 延迟的毫秒数);
方法四:
Handler