Android基础_Aron
2017-05-19 11:24:42 0 举报
AI智能生成
Android,工作中的一些总结,喜欢就点个赞吧
作者其他创作
大纲/内容
功能
数据存储
文件存储
网络存储
OkHttp+Glide
预加载,提前下载
preload(int width, int height)<br>preload(Y target)
允许开发者将Image的二进制文件下载到硬盘缓存当中,以便在后续使用
在后台线程当中下载图片,可以通过如下的方式:<br>Glide.with(applicationContext)<br> .load(yourUrl)<br> .preload(500, 500);<br>
下载完毕之后如果想要进行显示,可以通过如下方式进行调用:<br>Glide.with(yourFragment)<br> .load(yourUrl)<br> .diskCacheStrategy(DiskCacheStrategy.ALL)<br> .into(yourView);
通过DiskCacheStrategy.ALL或者DiskCacheStrategy.SOURCE,可以保证程序会去读取缓存文件
ListPreloader<br>如果你想让列表预加载的话,不妨试一下ListPreloader这个类。
缓存
子主题
调用diskCacheStrategy(DiskCacheStrategy.ALL)让Glide既缓存全尺寸又缓存其他尺寸
接口缓存
网络状态,netWorkUtils
Android安卓获取网络状态
数据库存储
共享参数
sharedPreferences简介
子主题
获取该对象的方式
调用Context对象的getSharedPreferences()方法
同一个context的组件都能使用
调用Activity对象的getPreferences()方法
该对象只能在该Activity中使用
四种操作模式
<div>Context.MODE_PRIVATE</div><div>Context.MODE_APPEND</div><div>Context.MODE_WORLD_READABLE</div><div>Context.MODE_WORLD_WRITEABLE</div>
<div>Context.MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容</div><div>Context.MODE_APPEND:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件.</div><div>Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE用来控制其他应用是否有权限读写该文件.</div><div>MODE_WORLD_READABLE:表示当前文件可以被其他应用读取.</div><div>MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入.</div>
存取数据
<div>SharedPreferences preferences=getSharedPreferences('user',Context.MODE_PRIVATE);</div><div>Editor editor=preferences.edit();</div><div>String name='xixi';</div><div>String age='22';</div><div>editor.putString('name', name);</div><div>editor.putString('age', age);</div><div>editor.commit(); //存数据 提交</div>
<div>SharedPreferences preferences=getSharedPreferences("user", Context.MODE_PRIVATE);</div><div>String name=preferences.getString("name", "defaultname");</div><div>String age=preferences.getString("age", "0"); 读取数据</div>
应用
是否第一次运行
<div>SharedPreferences sharedPreferences = this.getSharedPreferences("share", MODE_PRIVATE); </div><div>boolean isFirstRun = sharedPreferences.getBoolean("isFirstRun", true); </div><div>Editor editor = sharedPreferences.edit(); </div><div>if (isFirstRun) </div><div>{ </div><div>Log.d("debug", "第一次运行"); </div><div>editor.putBoolean("isFirstRun", false); </div><div>editor.commit(); </div><div>} else</div><div>{ </div><div>Log.d("debug", "不是第一次运行"); </div><div>}</div>
文件上传
选择文件
SD卡路径
SD卡权限
上传网络权限
Bitmap处理
byte[]<--->Bitmap
上传图片命名
通信(值回传)
startActivityForResult
广播
接口回调
推送
广播Receiver
广播声音振动
Notification自定义声音
代码执行点击
代码卸载
密钥
百度
SHA1 Android签名证书的sha1值+“;”+packagename(即:数字签名+分号+包名)
获取Android系统的唯一识别码
友盟
输入名称即可
极光
申请需要包名
包名替换
技能\技巧
Debug调试
<span style="color: rgb(51, 51, 51); font-family: 'black Verdana', Arial, Helvetica, sans-serif; font-size: 14px; line-height: 21px;">当程序运行到你的断点地方时就会停下,这时可以按照下面的功能键按需求进行调试:</span><br style="color: rgb(51, 51, 51); font-family: 'black Verdana', Arial, Helvetica, sans-serif; font-size: 14px; line-height: 21px;"><span style="color: rgb(51, 51, 51); font-family: 'black Verdana', Arial, Helvetica, sans-serif; line-height: 21px; font-size: 12px;">[1]快捷键(F8)直接执行程序,直到下一个断点处停止。<br>[2]快捷键(F5)单步执行程序,遇到方法时进入。<br>[3]快捷键(F6)单步执行程序,遇到方法时跳过。<br>[4]快捷键(F7)单步执行程序,从当前方法跳出。</span>
调试技巧
打印调用栈
三
// 调试打印堆栈而不退出<br> Log.d(TAG, Log.getStackTraceString(new Throwable()));
// 创建异常打印堆栈<br> Exception e = new Exception('this is a log');<br> e.printStackTrace();
// 获取当前线程的堆栈<br> for (StackTraceElement i : Thread.currentThread().getStackTrace()) {<br> Log.i(TAG, i.toString());<br> }
AS获取SHA1
<span style="color: rgb(47, 47, 47); font-family: -apple-system, 'SF UI Text', Arial, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'WenQuanYi Micro Hei', sans-serif; font-size: 16px; line-height: 30px;">进入到 .android 目录下,一般在 C:\Users\用户名\ 目录下</span>
<span style="color: rgb(101, 123, 131); font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; font-size: 12px; line-height: 30px; white-space: pre-wrap; background-color: rgb(246, 246, 246);">keytool -list -keystore debug.keystore</span>
<span style="color: rgb(47, 47, 47); font-family: -apple-system, 'SF UI Text', Arial, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'WenQuanYi Micro Hei', sans-serif; font-size: 16px; line-height: 30px;">这是会提示你输入秘钥口令,不用管它,直接确定就可以,如果不行就输入 android(这个是 debug.keystore 的默认秘钥口令)</span>
暗码
如user版本或者userdebug版本手机usb没有dai口话,请进入*#*#9738#*#*,把dai口的开关打开 <br>烧user版本的话,如需要打开root的权限的话,请进入*#*#9738#*#* 需要打开oemroot开关
Android版本
API
4.4 KITKAT = 19
4.4
奇巧
KITKAT_WATCH = 20
4.4W
21
5.0
Lollipop
22
5.1
Lollipop
5.1.1 Api22 LOLLIPOP_MR1
23
6.0
Marshmallow
棉花糖
6.0 Api23 M
24
7.0
Nougat
牛轧糖
7.0 Api24
16
25
7.1.1 Api
Nougat
161206: 加入类似“3D Touch”功能
26
Android8.0
O
Notification.setNumber 用于launcher显示数量
27
Android 8.1
O Oreo
奥利奥
28
Android 9.0
Pie
馅饼
29
Android 9.+
Q
新版本新特性
TextView
设置锁屏
Y3
修改默认桌面布局
1.改Launcher中的default_workspace.xml
2. Z:\zs_1.0\device\zeusis\Y3\cust\CUST-CHINA\
ALL\General\default_workspace.xml
CMCC\China\default_workspace.xml
CTCC\China\default_workspace.xml
CUCC\China\default_workspace.xml
GMS 演示版
验证桌布布局
push ..\default_workspace.xml data\cust\ 没用
已经root但提示仅读文件
cust 指向 cust/ALL/General
改文件模式
没用
push ..\default_workspace.xml cust/ALL/General
rm data/data/com.journeyui.zslauncher
重启
背屏应用
<div>Y3:/system/presetapp # ls</div><div>AiQiYi IreaderA MiguA ShangYeZhouKangB TongHuaShunA YingYongBao</div><div>Amap IreaderB MiguB SinaWeibo TongHuaShunB iReader</div><div>BaiduInput JieMian NewsRepublic_ares_ww SogouSearch_pollux Toutiao_pollux letvplayer</div><div>BaiduSearch JieMianA QQReaderA SohuNewsClient_pollux VoiceAssistant letvplayer_pollux</div><div>BaoLiTest Jingdong_pollux QQReaderB TecentNews WPSOffice</div><div>CloudMusic_cmcc KuwoPlayer QQTongBuZhuShou TencentVideo Wechat</div><div>Ctrip MeiTuan Qunar_pollux TencentVideo_pollux Weibo_pollux</div><div>Happyelements_pollux Meituan_pollux ShangYeZhouKangA TencentWifiManager Wps</div>
插件化
问题
<div>问题:插件不在BLauncher当前显示页时,也会在后台同步数据或弹dialog或弹toast</div><div>问题产生原因:BLauncher使用的是ViewPager,滑动时会预加载多个插件</div><div>要求:</div><div> 1.当滑出插件页时,主动dismiss已弹dialog</div><div> 2.当滑出插件页时,主动让显示的toast消失</div><div> 3.当滑入插件页时,才能弹dialog或Toast</div><div> 4.当滑出插件页时,主动停止界面的数据刷新和后台的数据同步</div><div>技术方案:</div><div> 1.滑入插件页时,BLauncher会调用插件的switchToThisPage()方法</div><div> 2.滑出插件页时,BLauncher会调用插件的switchOffThisPage()方法</div>
您好,如果背屏插件页使用Dialog和popWindow时,请不要处理返回键,导致点返回键不能dismiss窗口导致白屏的情况(只针对插件页,二级页面随意处理),<br>还请检查下插件页的代码是否使用了弹窗并屏蔽了返回键,排查完还请回复一下<br><br>
系统
子主题
包名
子主题 1
android获取已安装应用的安装包
拨号
静态语句块,语句块 构造方法
资源文件
Drawable
弹框消失
键盘
隐藏键盘
搜索的时候 获取键盘的回车
颜色相关
android 在代码中使用 #ffffff 模式 设置背景色
4种设置颜色
Color.rgb(10, 210, 25)
返回int
Color.parseColor("#dd0000")
常用颜色
子主题 4
在使用shape的同时,用代码修改shape的颜色属性
使用资源文件的颜色
透明度
组件
Activity
值回传
回传到Fragment
值回传
移除之前的所有activity
更改背景透明度
Intent
跳多个intent
启动另一个App
OnKeyDown
启动背屏
<div>Intent intent = new Intent(getContext(), JvMainActivity.class);</div><div> intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);</div><div> intent.setAction(Intent.ACTION_MAIN);</div><div> intent.addCategory(Intent.CATEGORY_LAUNCHER);</div><div> intent.addCategory(Intent.CATEGORY_HOME);</div><div> getContext().startActivity(intent);</div>
<div>Intent intent = new Intent(JvLApplication.getInstance(), JvMainActivity.class);</div><div> intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);</div><div> intent.setAction(Intent.ACTION_MAIN);</div><div> intent.addCategory(Intent.CATEGORY_LAUNCHER);</div><div> intent.addCategory(Intent.CATEGORY_HOME);</div><div> JvLApplication.getInstance().startActivity(intent);</div>
最近任务的方式启动应用
通过taskId启动任务栈
获取运行的task
ActivityManager m = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
<br>List<runningtaskinfo> runningTaskInfoList = m.getRunningTasks(1);
<br>if(!runningTaskInfoList.isEmpty()) {
<br> String callingPackageName = runningTaskInfoList.get(0).baseActivity.getPackageName();
<br>}</runningtaskinfo>
ContentProvider<br>内容提供者
简介
基本使用
系统联系人
查询
private Cursor query(String name) {<br><br> ContentResolver cr = getContentResolver();<br> Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;<br><br> //查询对象<br> String[] projection = {ContactsContract.Contacts._ID, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER};<br> //设置查询条件为空<br> String selection = null;<br> String[] selectionArgs = null;<br> if (!''.equals(name)) {<br><br> selection = ContactsContract.Contacts.DISPLAY_NAME + '=?';<br> selectionArgs = new String[]{name};<br><br> }<br> String sortOrder = ContactsContract.Contacts._ID;<br> Cursor c = cr.query(uri, projection, selection, selectionArgs, sortOrder);<br><br> try {<br> while (c.moveToNext()) {<br> String name1 = c.getString(c.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME));<br> String number = c.getString(c.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER));<br> Log.i(TAG, 'query: name = ' + name1 + ', number = ' + number);<br> }<br> } catch (Exception e) {<br> e.printStackTrace();<br> } finally {<br> if (c != null) {<br> c.close();<br> }<br> }<br> return c;<br> }
权限
<uses-permission android:name="android.permission.READ_CONTACTS"></uses-permission><br>
当联系人应用加密时,也不能读取成功
插入
自定义ContentProvider
新建一个类继承ContentProvider
AndroidManifest.xml
声明provider
android:export=true
api16以上默认false
广播
广播类型
静态注册
<div>优点:不受应用生命周期的影响,常驻 </div><div>缺点:常驻会耗费cpu、电量等资源</div>
动态注册
<div>优点:在Android的广播机制中,动态注册的优先级高于静态注册的优先级,因此在必要情况下,我们需要动态注册广播接收器。取消注册后,不再占用资源 </div><div>缺点:程序退出,注销广播后,便无法进行广播监听</div>
示例
存储不足
<div>在sendNotification()中将剩余内存的值存入intent中, 然后广播出去, 广播类型为ACTION_MANAGE_PACKAGE_STORAGE</div><div>此通知的标题为 “存储空间不足(Low on space)”</div><div>通知的内容为 “手机内存空间所剩不多了(Phone storage space is getting low)”</div>
Service
启动startService
简介
所谓的started service, 是我对以startService()方法启动的service的叫法. Service运行在所在进程的main thread中. 启动一个service时, 不会自动为该service创建新的thread. 这意味着开发者通常需要为service开启新的线程, 以执行耗时或者阻塞操作—否则可能导致ANR错误的发生. 既然如此, 为何不在activity中直接开启新的线程执行耗时操作或者阻塞操作呢? 原因在于一个包含正在运行的service的进程具有更安全的进程优先级--它的进程优先级至少是service process, 而activity处于后台运行时它的进程优先级为background process, 在系统内存不足时, 处于后台运行的activity更有可能被系统杀死. 关于进程优先级, 在我的另一篇博文中有过介绍http://coolxing.iteye.com/blog/1279170.
<div>如果没有调用stopService()方法或者stopSelf()方法, 就算onStartCommand()方法执行完成, 该service仍然处于active状态, 但onStartCommand()方法返回后, 系统可能处于内存不足的缘故摧毁这个service, 如果发生这种情形, 那么系统将尽快重建这个service, 而onStartCommand()方法的返回值用来定义系统该如何重建service, 返回值的可以是以下3个int值中的一个:</div><div>START_NOT_STICKY, 表明不要重建service. 这可以避免在非必要的情况下浪费系统的资源.</div><div>START_STICKY, 表明需要重建service, 并在重建service之后调用onStartCommand()方法, 传递给该方法的intent为null.</div><div>START_REDELIVER_INTENT, 表明需要重建service, 并在重建service之后调用onStartCommand()方法, 传递给该方法的intent为service被摧毁之前接收到的最后一个intent. </div><div>当service第一次被启动时会调用onCreate()方法, 然后再调用onStartCommand()方法. 在该service的生命周期内, 如果再次启动这个service, 就会直接调用onStartCommand()方法了.</div><div>onStartCommand(Intent intent, int flags, int startid)方法的第一个参数intent就是用于启动该service的intent, 第二个参数flags的值通常为0, 第三个参数startid标识此次启动请求, 通常用于stopSelf(int)方法.</div><div>通常情况下, started service应该使用单线程处理多个启动请求, 此时继承IntentService是一个更好的选择, 当然也可以继承Service类, 只是会导致更多的代码. 不过如果started service需要并发处理多个启动请求, 那么只能继承Service类.</div>
绑定bindService
四大组件之间的通信
Activity和Service
通过Binder对象
即bindServcie启动
广播接收器
如果有多个Acitivity注册接收,建议采用这种方式
属性配置
手机IMSI/IMEI
IMSI
String android_imsi = telephonyManager.getSubscriberId();//获取手机IMSI号 <br> String IMSI = android.os.SystemProperties.get( android.telephony.TelephonyProperties.PROPERTY_IMSI);
IMEI
<div>唯一的设备ID: GSM手机的 IMEI 和 CDMA手机的 MEID. </div>
String imei = ((TelephonyManager) context.getSystemService(TELEPHONY_SERVICE)).getDeviceId();<br>
<span>在manifest.xml文件中要添加 <uses-permission android:name="android.permission.READ_PHONE_STATE" /></span>
Y3得到的IMEI是null
如果Android Pad没有IMEI,用此方法获取设备ANDROID_ID:
String android_id = Secure.getString(this.getContentResolver(), Secure.ANDROID_ID);
String IMEI = android.os.SystemProperties.get(android.telephony.TelephonyProperties.PROPERTY_IMEI)
不过纯APP开发SystemProperties,TelephonyProperties汇报错误,因为android.os.SystemProperties在SDK的库中是没有的,需要把Android SDK 目录下data下的layoutlib.jar文件加到当前工程的附加库路径中,就可以Import。
String imei = Settings.System.getString(context.getContentResolver(), Settings.System.ANDROID_ID);
手机号
TelephonyManager tm= (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);<br>String number = tm.getLine1Number();
版本号
AndroidManifest.xml<br>android:versionCode="1"<br>android:versionName="1.0"
子主题
手机型号
String DeviceName = android.os.Build.DEVICE
工具
ADB
adb被占用时
Mac ADB
MX4 Pro ADB
查看签名指纹证书(SHA1)
keytool -list -keystore E:\Turbo\AndroidStudioProjects\mykey.jks
keytool -list -keystore .\keystore\platform.jks
keytool -list -keystore mykey_new.store
keytool在
事件分发
场景1
问题: frameLayout 或ViewGroup事件穿透到下层
需要拦截事件给自己或子类消费
setClickable true
android:clickable="true" <br>
把父类的touch事件分给自己处理
ViewGroup view = (ViewGroup) mVpGallery.getParent();<br> view.setOnTouchListener(new View.OnTouchListener() {<br> @Override<br> public boolean onTouch(View view, MotionEvent motionEvent) {<br> return mVpGallery.dispatchTouchEvent(motionEvent);<br> }<br> });
View相关
View转Bitmap
问题:buildDrawingCache()导致ViewGroup中child.mFlagView为空
drawableId转 Bitmap
BitmapFactory.decodeResource(getResources(), R.drawable.ic_archive_24dp)
自定义宽高和模式ARGB_8888 or RGB_565
基础
数据存储
文件存储
[Android入门:File文件存储]
文件目录
context或Activity
getFilesDir()方法用于获取/data/data//files目录
getAbsolutePath()
获取绝对路径
getPath()
getCacheDir()方法用于获取/data/data//cache目录
Environment.getExtemalStorageDirectory()
获取SDCard的目录
SD卡状态
Environment.getExtemalStorageState()<br>
Environment.MEDIA_MOUNTED
手机装有SDCard,并且可以进行读写
简单示例
File saveFile=new File("/sdcard/zhzhg.txt");<br>或:File sdCardDir=new File("/sdcard");//获取SD卡目录<br>File saveFile = new File(sdCardDir,"zhzhg.txt");<br>FileOutputStream outStream = new FileOutputStream(saveFile);<br>outStream.write("文件的读写".getBytes());<br>outStream.close();
文件模式Mode
<div>二、文件模式介绍</div><div>1.Context.MODE_PRIVATE:私有覆盖模式 </div><div>- rw- rw- ---</div><div>只能被当前应用访问,并且如果写入,则覆盖;</div><div>2.Context.MODE_APPEND:私有追加模式 </div><div>- rw- rw- ---</div><div>只能被当前应用访问,并且如果写入,则追加;</div><div>3.Context,MODE_WORLD_READABLE:公有只读模式 - rw- rw- r--</div><div>可以被其他应用读取;</div><div>4.Context.MODE_WORLD_WRITEABLE:公有可写模式 - rw- rw- -w-</div><div>可以被其他应用写入,但不能读取;</div>
<div>注意,如果希望其他使得文件模式叠加,则可以使用加号连接;</div><div>比如:Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE 表示其他应用读写;</div>
chmod 777 fileName
文件改成 rwx rwx rwx
文件操作
计算文件夹大小
<div>/** </div><div> * 获取文件夹大小 </div><div> * @param file File实例 </div><div> * @return long </div><div> */ </div><div> public static long getFolderSize(java.io.File file){ </div><div> </div><div> long size = 0; </div><div> try { </div><div> java.io.File[] fileList = file.listFiles(); </div><div> for (int i = 0; i < fileList.length; i++) </div><div> { </div><div> if (fileList[i].isDirectory()) </div><div> { </div><div> size = size + getFolderSize(fileList[i]); </div><div> </div><div> }else{ </div><div> size = size + fileList[i].length(); </div><div> </div><div> } </div><div> } </div><div> } catch (Exception e) { </div><div> // TODO Auto-generated catch block </div><div> e.printStackTrace(); </div><div> } </div><div> //return size/1048576; </div><div> return size; </div><div> } </div>
格式化单位
<div>/** </div><div> * 格式化单位 </div><div> * @param size </div><div> * @return </div><div> */ </div><div> public static String getFormatSize(double size) { </div><div> double kiloByte = size/1024; </div><div> if(kiloByte < 1) { </div><div> return size + "Byte(s)"; </div><div> } </div><div> </div><div> double megaByte = kiloByte/1024; </div><div> if(megaByte < 1) { </div><div> BigDecimal result1 = new BigDecimal(Double.toString(kiloByte)); </div><div> return result1.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "KB"; </div><div> } </div><div> </div><div> double gigaByte = megaByte/1024; </div><div> if(gigaByte < 1) { </div><div> BigDecimal result2 = new BigDecimal(Double.toString(megaByte)); </div><div> return result2.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "MB"; </div><div> } </div><div> </div><div> double teraBytes = gigaByte/1024; </div><div> if(teraBytes < 1) { </div><div> BigDecimal result3 = new BigDecimal(Double.toString(gigaByte)); </div><div> return result3.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "GB"; </div><div> } </div><div> BigDecimal result4 = new BigDecimal(teraBytes); </div><div> return result4.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "TB"; </div><div> } </div>
文件Copy
<div>private static boolean copy(File source, File target) {</div><div> FileInputStream in = null;</div><div> FileOutputStream out = null;</div><div> try {</div><div> if(!target.exists()){</div><div> boolean createSucc = target.createNewFile();</div><div> if(!createSucc){</div><div> return false;</div><div> }</div><div> }</div><div> in = new FileInputStream(source);</div><div> out = new FileOutputStream(target);</div><div> byte[] buffer = new byte[8*1024];</div><div> int count;</div><div> while ((count = in.read(buffer)) != -1) {</div><div> out.write(buffer, 0, count);</div><div> }</div><div> return true;</div><div> } catch (Exception e) {</div><div> e.printStackTrace();</div><div> Log.e(TAG, e.getMessage(), e);</div><div> recordLogServiceLog("copy file fail");</div><div> return false;</div><div> } finally{</div><div> try {</div><div> if(in != null){</div><div> in.close();</div><div> }</div><div> if(out != null){</div><div> out.close();</div><div> }</div><div> } catch (IOException e) {</div><div> e.printStackTrace();</div><div> Log.e(TAG, e.getMessage(), e);</div><div> recordLogServiceLog("copy file fail");</div><div> return false;</div><div> }</div><div> }</div><div><br></div><div> }</div>
拷贝assets目录下文件
InputStream is = ctx.getAssets().open("test.apk");
文件压缩
GZipOutputStream
BufferedOutputStream out = new BufferedOutputStream(new GZIPOutputStream(new FileOutputStream("test.txt.gz")));
追加写入文件内容
追加文件:使用FileOutputStream,在构造FileOutputStream时,把第二个参数设为true
<div>new OutputStreamWriter( </div><div> new FileOutputStream(file, true)))</div>
<div>// 打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件 </div><div> FileWriter writer = new FileWriter(fileName, true); </div>
文件重命名
<div>File file = new File(oldPath);</div><div> file.renameTo(new File(newPath));</div>
<div>第一,如果是Android的SD卡上的文件重命名,那么必须添加权限:</div><div><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /></div><div>第二,oldPath和newPath必须是新旧文件的绝对路径</div>
文件上传
选择照片
子主题
选择文件
OkHttp上传
无进度条
<div>public static void uploadFile(final String url, final Map<String, Object> params, File file) {</div><div> OkHttpClient client = new OkHttpClient();</div><div> // form 表单形式上传</div><div> MultipartBody.Builder requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM);</div><div> if(file != null){</div><div> // MediaType.parse() 里面是上传的文件类型。</div><div> RequestBody body = RequestBody.create(null, file);</div><div>// RequestBody body = RequestBody.create(MediaType.parse("image/*"), file);//图片文件</div><div>// String filename = file.getName();</div><div> // 参数分别为, 请求key ,文件名称 , RequestBody</div><div> requestBody.addFormDataPart("file", file.getName(), body);</div><div> }</div><div><br></div><div> // 生成带token的url,并在params中加入3个参数</div><div> String urlMD5 = UrlBuilder.decorateCommonParams(url, null, params);</div><div><br></div><div> if (params != null) {</div><div> // map 里面是请求中所需要的 key 和 value</div><div> for (Map.Entry entry : params.entrySet()) {</div><div> requestBody.addFormDataPart(valueOf(entry.getKey()), valueOf(entry.getValue())).setType(MultipartBody.MIXED);</div><div> }</div><div> }</div><div> Log.i(TAG, "uploadFile: urlMD5 = " + urlMD5);</div><div> Request request = new Request.Builder().url(urlMD5).post(requestBody.build()).build();</div><div> // readTimeout("请求超时时间" , 时间单位);</div><div> client.newBuilder().readTimeout(500, TimeUnit.SECONDS).build().newCall(request).enqueue(new Callback() {</div><div> @Override</div><div> public void onFailure(Call call, IOException e) {</div><div> uploadFailure(call, e);</div><div> }</div><div><br></div><div> @Override</div><div> public void onResponse(Call call, Response response) throws IOException {</div><div> if (response.isSuccessful()) {</div><div> uploadSuccess(call, response);</div><div><br></div><div> } else {</div><div> uploadNotSuccess(call, response);</div><div> }</div><div> }</div><div> });</div><div><br></div><div> }</div>
带进度条
<div> /**</div><div> * 创建带进度的RequestBody</div><div> * @param contentType MediaType</div><div> * @param file 准备上传的文件</div><div> * @param callBack 回调</div><div> * @param <T></div><div> * @return</div><div> */</div><div> public <T> RequestBody createProgressRequestBody(final MediaType contentType, final File file, final ReqProgressCallBack<T> callBack) {</div><div> return new RequestBody() {</div><div> @Override</div><div> public MediaType contentType() {</div><div> return contentType;</div><div> }</div><div><br></div><div> @Override</div><div> public long contentLength() {</div><div> return file.length();</div><div> }</div><div><br></div><div> @Override</div><div> public void writeTo(BufferedSink sink) throws IOException {</div><div> Source source;</div><div> try {</div><div> source = Okio.source(file);</div><div> Buffer buf = new Buffer();</div><div> long remaining = contentLength();</div><div> long current = 0;</div><div> for (long readCount; (readCount = source.read(buf, 2048)) != -1; ) {</div><div> sink.write(buf, readCount);</div><div> current += readCount;</div><div>// Log.e(TAG, "current------>" + current);</div><div> progressCallBack(remaining, current, callBack);</div><div> }</div><div> } catch (Exception e) {</div><div> e.printStackTrace();</div><div> }</div><div> }</div><div> };</div><div> }</div><div><br></div><div> public interface ReqProgressCallBack<T> extends ReqCallBack<T>{</div><div> /**</div><div> * 响应进度更新</div><div> */</div><div> void onProgress(long total, long current);</div><div> }</div><div><br></div><div> public interface ReqCallBack<T> {</div><div> void onSucess();</div><div> void onFail();</div><div> }</div><div><br></div><div> public void progressCallBack(long total, long current, ReqProgressCallBack callBack){</div><div> callBack.onProgress(total, current);</div><div> }</div>
设计模式
单例模式
恶汉式-简单快捷
<div>public class Singleton{</div><div> //在自己内部定义自己的一个实例,只供内部调用</div><div> private static final Singleton instance = new Singleton();</div><div> private Singleton(){</div><div> //do something</div><div> }</div><div> //这里提供了一个供外部访问本class的静态方法,可以直接访问</div><div> public static Singleton getInstance(){</div><div> return instance;</div><div> }</div><div>}</div>
instance在类装载时就实例化,虽然导致类装载的原因有很多种在单例模式中大多数都是调用getInstance方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到lazy loading的效果。
恶汉式-变种
<div>public class Singleton { </div><div> private Singleton instance = null; </div><div> static { </div><div> instance = new Singleton(); </div><div> } </div><div> private Singleton (){} </div><div> public static Singleton getInstance() { </div><div> return this.instance; </div><div> } </div><div>} </div>
表面上看起来差别挺大,其实跟简单恶汉式差不多,都是在类初始化即实例化instance。
懒汉式-线程安全
<div>public class SingletonClass{</div><div> private static volatile SingletonClass instance=null;</div><div> public static SingletonClass getInstance(){</div><div> synchronized(SingletonClass.class){</div><div> if(instance==null){</div><div> instance=new SingletonClass();</div><div> }</div><div> }</div><div> return instance;</div><div> }</div><div> private SingletonClass(){}</div><div>}</div>
<div>public class SingletonClass{</div><div> private static SingletonClass instance=null;</div><div> public static synchronized SingletonClass getInstance(){</div><div> if(instance==null){</div><div> instance=new SingletonClass();</div><div> }</div><div> return instance;</div><div> }</div><div> private SingletonClass(){</div><div> }</div><div>}</div>
双重校验锁-提高效率
<div>public class Singleton{</div><div> private static volatile Singleton instance=null;</div><div> private Singleton(){</div><div> //do something</div><div> }</div><div> public static Singleton getInstance(){</div><div> if(instance==null){</div><div> synchronized(SingletonClass.class){</div><div> if(instance==null){</div><div> instance=new Singleton();</div><div> }</div><div> }</div><div> }</div><div> return instance;</div><div> }</div><div>}</div>
懒汉式-线程不安全
<div>public class Singleton { </div><div> private static Singleton instance; </div><div> private Singleton (){} </div><div> </div><div> public static Singleton getInstance() { </div><div> if (instance == null) { </div><div> instance = new Singleton(); </div><div> } </div><div> return instance; </div><div> } </div><div>} </div>
关闭进程
强制关闭进程
<div>try {</div><div> final String command = "am force-stop com.android.jv.ink.launcherink";</div><div> Process process = Runtime.getRuntime().exec(command);</div><div> } catch (IOException e) {</div><div> e.printStackTrace();</div><div> }</div>
关闭
android.os.Process.killProcess(android.os.Process.myPid());
进程没有完全杀死,会导致其他问题,如:之前的数据还在,显示了不想要的数据
系统
时间日期
UI控件
子主题
获取时间
Date
设置当前时间
<div>SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");<br></div><div>timeTextView.setText(sdf.format(new Date()));</div>
当前时间戳
String.valueOf(System.currentTimeMillis())
Calendar
<div>Calendar c = Calendar.getInstance();</div><div><br></div><div>int curYear = c.get(Calendar.YEAR);</div><div>curMonth = c.get(Calendar.MONTH) + 1;</div><div>curHours = c.get(Calendar.HOUR_OF_DAY);</div>
定时器
2s不可点击
long mBtnLastClick = 0; // 字段
<div> if (System.currentTimeMillis() - mBtnLastClick < 2000) {</div><div> return;</div><div> }</div><div> mBtnLastClick = System.currentTimeMillis();</div>
2s后执行
<div>Handler + Runnable</div>
Timer+TimerTask
开个子线程sleep2秒
定时重复任务
Timer & TimerTask
基本使用
<div>Timer</div><div>Android 的 Timer 类可以用来计划需要循环执行的任务,Timer 的问题是它需要用 WakeLock 让 CPU 保持唤醒状态,这样会大量消耗手机电量,大大减短手机待机时间。这种方式不能满足我们的需求</div>
AlarmManager
基本使用
<div>AlarmManager</div><div>AlarmManager 是 Android 系统封装的用于管理 RTC 的模块,RTC (Real Time Clock) 是一个独立的硬件时钟,可以在 CPU 休眠时正常运行,在预设的时间到达时,通过中断唤醒 CPU。</div><div>这意味着,如果我们用 AlarmManager 来定时执行任务,CPU 可以正常的休眠,只有在需要运行任务时醒来一段很短的时间。</div>
JobSchedule
安卓5.0以后为了省电
Handler + Runnable<br>2s后重复执行
1. 定义一个Handler类<br>[java] view plaincopy<br>Handler handler=new Handler(); <br>Runnable runnable=new Runnable() { <br> @Override <br> public void run() { <br> // TODO Auto-generated method stub <br> //要做的事情 <br> handler.postDelayed(this, 2000); //每两秒执行一次runnable. <br> } <br>};
2. 启动计时器<br>[java] view plaincopy<br>handler.postDelayed(runnable, 2000); //2s后执行<br>
3. 停止计时器<br>[java] view plaincopy<br>handler.removeCallbacks(runnable);
第三方库的选择
事件分发如EventBus,RxEvent
Gradle
功能
1.多渠道打包
<div>AndroidManifest.xml中修改以下值:</div><div><meta-data android:name="UMENG_CHANNEL" android:value="wandoujia" /></div><div>首先你必须在AndroidManifest.xml中的meta-data修改以下的样子:</div><div><meta-data android:name="UMENG_CHANNEL" android:value="${UMENG_CHANNEL_VALUE}" /></div>
productFlavors { wandoujia {}baidu {}c360 {}uc {} <br>productFlavors.all { flavor ->flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]} }
这样生成apk时,选择相应的Flavors来生成指定渠道的包就可以了,而且生成的apk会自动帮你加上相应渠道的后缀,非常方便和直观。大家可以自己反编译验证。
<div>在android studio底栏中有个命令行工具Terminal,打开后就CMD可以自动切换到当前项目的目录下。</div><div>有的项目下会有graldew.bat这个文件,你就可以输入这个命令:</div><div>gradlew assembleRelease就可以一次性生成所有的渠道包了不过我一般不建议大家使用这个命令,gradlew这个命令的gralde的版本无法控制,有时候会莫名其妙的下载老版本的gradle所以我个人推荐大家使用以下的用法。</div>
<div>先找到gralde的根目录,在系统变量里添加两个环境变量:</div><div><br></div><div>变量名为:GRADLE_HOME,变量值就为gradle的根目录;所以变量值为:C:\Users\yazhou\.gradle\wrapper\dists\gradle-2.1-all\27drb4udbjf4k88eh2ffdc0n55\gradle-2.1</div><div><br></div><div>还有一个在系统变量里PATH里面添加gradle的bin目录我的就是C:\Users\yazhou\.gradle\wrapper\dists\gradle-2.1-all\27drb4udbjf4k88eh2ffdc0n55\gradle-2.1\bin</div><div><br></div><div>这里配置完成了,接着在Terminal中敲下 gradle assembleRelease就可以一次性生成所有的渠道包了。</div><div><br></div><div>所有生成的apk在项目的build\outputs\apk下。</div>
<div>如果只是想生成单个渠道的包呢?</div><div>打开Android Studio的Gradle tasks面板(右边侧边栏),会发现模块多了很多任务,如下图所示。</div>
多渠道配置源码路径
<div> sourceSets {</div><div><br></div><div> // userDebug版本</div><div> userDebug {</div><div>// manifest.srcFile 'src/coolYota/AndroidManifest.xml'</div><div>// java.srcDir 'src/usefull/yotadevice/java'</div><div> }</div><div><br></div><div> // 用户版本</div><div> user {</div><div>// java.srcDir 'src/unusefull/yotadevice/java'</div><div> }</div><div> }</div>
Maven仓库
上传到本地指定目录
<div>apply plugin: 'com.android.library'</div><div>apply plugin: 'maven'</div>
<div>def artifactId = 'common'</div><div>def groupId = "com.baoliyota.lib"</div><div>def version = '1.0.0'</div><div><br></div><div>uploadArchives {</div><div> repositories {</div><div> mavenDeployer {</div><div>// snapshotRepository(url: "http://172.16.55.160:8081/repository/maven-snapshot/") {</div><div>// authentication(userName: nexusUsername, password: nexusPassword)</div><div>// }</div><div> pom.artifactId = artifactId</div><div> pom.groupId = groupId</div><div> pom.version = version</div><div> pom.project {</div><div> packaging 'aar'</div><div> description 'common for by'</div><div> }</div><div><br></div><div> def devRoot = System.getenv("BY_DEV_ROOT")</div><div> if (devRoot == null || "".equals(devRoot)) {</div><div> devRoot = file("./../../..").toString()</div><div> }</div><div> repository(url: "file://${devRoot}/android/distribute/libs")</div><div> snapshotRepository(url: "file://${devRoot}/android/distribute/libs")</div><div> }</div><div> }</div><div>}</div>
会在项目同级目录生成android/..
需要引入的项目也需要在同级目录
其他项目使用
项目
build.gradle
<div>buildscript {</div><div> repositories {</div><div> jcenter()</div><div> }</div><div> dependencies {</div><div> classpath 'com.android.tools.build:gradle:2.2.2'</div><div><br></div><div> // NOTE: Do not place your application dependencies here; they belong</div><div> // in the individual module build.gradle files</div><div> }</div><div>}</div><div><br></div><div>allprojects {</div><div><br></div><div> repositories {</div><div><br></div><div> def devRoot = System.getenv("BY_DEV_ROOT")</div><div> if (devRoot == null || "".equals(devRoot)) {</div><div> devRoot = file("./../../..").toString()</div><div> }</div><div> if (file(devRoot).isDirectory()) {</div><div> maven {</div><div> url "file://${devRoot}/android/distribute/libs/"</div><div> }</div><div> }</div><div><br></div><div> jcenter()</div><div> }</div><div>}</div><div><br></div><div>task clean(type: Delete) {</div><div> delete rootProject.buildDir</div><div>}</div>
app
build.gradle
<div>repositories {</div><div><br></div><div> def devRoot = System.getenv("BY_DEV_ROOT")</div><div> if (devRoot == null || "".equals(devRoot)) {</div><div> devRoot = file("./../../..").toString()</div><div> }</div><div> if (file(devRoot).isDirectory()) {</div><div> maven {</div><div> url "file://${devRoot}/android/distribute/libs/"</div><div> }</div><div> }</div><div> }</div>
compile 'com.baoliyota.lib:common:1.0.0@aar'
compile 'com.coolyota.lib:analysis:1.0.0@aar'
上传到服务器
upload.gradle
<div>apply plugin: 'maven'</div><div><br></div><div>task androidJavadocs(type: Javadoc) {</div><div> source = android.sourceSets.main.java.srcDirs</div><div> classpath += project.files(android.getBootClasspath().join(File.pathSeparator))</div><div>}</div><div><br></div><div>task androidSourcesJar(type: Jar) {</div><div> classifier = 'sources'</div><div> from android.sourceSets.main.java.srcDirs <span>//</span><span>生成在</span><span>build/libs/</span><span>目录下</span></div><div>}</div><div><br></div><div>artifacts {</div><div> archives androidSourcesJar</div><div>}</div><div><br></div><div>//任务名</div><div>uploadArchives {</div><div> repositories {</div><div> mavenDeployer {</div><div> //这里的url是nexus中maven-releases的路径,可以点击copy按钮查看复制</div><div> repository(url: "http://localhost:8081/repository/maven-releases/") {</div><div> // nexus账号的用户名和密码,我这里没用默认的admin</div><div> authentication(userName: "jcking", password: "jcking123")</div><div> }</div><div><br></div><div> // 下面这三项pom参数,在调用的时候是这个样子 : compile 'com.jcking.jbottomtabbar:jbottomtabbar:0.0.1'</div><div> // library的包名</div><div> pom.groupId = 'com.jcking.jbottomtabbar'</div><div> // library的项目名</div><div> pom.artifactId = 'jbottomtabbar'</div><div> // library的版本号</div><div> pom.version = '0.0.1'</div><div><br></div><div> pom.project {</div><div> licenses {</div><div> license {</div><div> name 'The Apache Software License, Version 2.0'</div><div> url 'http://www.apache.org/licenses/LICENSE-2.0.txt'</div><div> }</div><div> }</div><div> }</div><div> }</div><div> }</div><div>}</div>
build.gradle
指定output输出路径
根据buildType不同输出文件
<div>android.applicationVariants.all { variant -></div><div> variant.outputs.each { output -></div><div> def outputFile = output.outputFile</div><div> println("outputFile = " + outputFile)</div><div> if (outputFile != null && outputFile.name.endsWith('.apk')) {</div><div> def fileName</div><div> if (variant.buildType.name.equals('release')) {</div><div> fileName = "CY_Log_Reporter_user.apk"</div><div> } else if (variant.buildType.name.equals('debug')) {</div><div> fileName = "CY_Log_Reporter.apk"</div><div> }</div><div> output.outputFile = new File(outputFile.parent, fileName)</div><div> }</div><div> }</div><div> }</div>
<div>android.applicationVariants.all { variant -></div><div> variant.outputs.each { output -></div><div> def outputFile = output.outputFile</div><div> println("outputFile = " + outputFile)</div><div> if (outputFile != null && outputFile.name.endsWith('.apk')) {</div><div> def fileName</div><div> if (variant.buildType.name == 'release') {</div><div> fileName = "CY_Log_Reporter_user.apk"</div><div> } else if (variant.buildType.name == 'debug') {</div><div> fileName = "CY_Log_Reporter.apk"</div><div> }</div><div> output.outputFile = new File(outputFile.parent, fileName)</div><div> }</div><div> }</div><div> }</div>
根据文件后缀区分debug和release,<br>输出到前3级目录app/build/outputs/apk<br>输出到app/目录下
<div> android.applicationVariants.all { variant -></div><div> variant.outputs.each { output -></div><div> def outputFile = output.outputFile</div><div> println("outputFile = " + outputFile)</div><div> if (outputFile != null && outputFile.name.endsWith('app-debug.apk')) {</div><div> // 输出apk名称为CY_Log_Reporter_v1.0_2015-01-15_coolyota.apk</div><div> def fileName = "CY_Log_Reporter.apk"</div><div>// def fileName = "CY_Log_Reporter_v${defaultConfig.versionName}_${releaseTime()}_${variant.productFlavors[0].name}.apk"</div><div> output.outputFile = new File(outputFile.parent + "/../../../", fileName)</div><div> } else if (outputFile != null && outputFile.name.endsWith('app-release-unsigned.apk')) {</div><div><br></div><div> def fileName = "CY_Log_Reporter_user.apk"</div><div> output.outputFile = new File(outputFile.parent + "/../../../", fileName)</div><div> }</div><div> }</div><div> }</div>
<div>android.applicationVariants.all { variant -></div><div> variant.outputs.each { output -></div><div> def outputFile = output.outputFile</div><div> if (outputFile != null && (outputFile.name.endsWith('yotaDevice-debug.apk')</div><div> || outputFile.name.endsWith('app-yotaDevice-release.apk'))) {</div><div> //这里修改apk文件名 yotaDevice-debug</div><div> def fileName = "CY_BLauncher.apk"</div><div> output.outputFile = new File(outputFile.parent, fileName)</div><div> }</div><div> }</div><div> }</div>
生成SourceJar
<div>task androidSourcesJar(type: Jar) {</div><div> classifier = 'sources'</div><div>// println("classifier = " + classifier)</div><div> from android.sourceSets.main.java.srcDirs //生成在build/libs/目录下</div><div>}</div>
右方Gradle-当前的moudle-Task-Other-androidSourcesJar 右键Run
生成JavaDoc
<div>/*task androidJavadocs(type: Javadoc) {//第三方jar报错如OKHttp,自动生成的Java如BuildConfig报错</div><div> title = " common API"</div><div> source = sourceSets.main.allJava</div><div> destinationDir = file("Z:\\javadocs")</div><div> source = android.sourceSets.main.java.srcDirs</div><div> classpath += project.files(android.getBootClasspath().join(File.pathSeparator))</div><div>}*/</div><div><br></div><div>//androidJavadocs {</div><div>// options {</div><div>// encoding "UTF-8"</div><div>// charSet 'UTF-8'</div><div>// author true</div><div>// version true</div><div>// links "http://docs.oracle.com/javase/7/docs/api"</div><div>// }</div><div>//}</div>
Your Moudle的build->outputs->docs->javadoc中找到文档了
右方Gradle-当前的moudle-Task-Other-androidJavadocs 右键Run
方法常量
获取时间
<div>def releaseTime() {<br></div><div> return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))</div><div>}</div>
打印
println()
定义常量
def fileName
依赖包
谷歌
<div>compile 'com.android.support:appcompat-v7:25.3.1'</div><div>compile 'com.android.support.constraint:constraint-layout:1.0.2'</div><div>compile 'com.android.support:design:25.1.0' </div><div> compile 'com.android.support:support-v4:25.3.1'</div><div> compile 'com.android.support:preference-v14:25.3.1'</div>
<div>compile 'com.android.support:appcompat-v7:25.3.1'</div><div>compile 'com.android.support.constraint:constraint-layout:1.0.2'</div><div>compile 'com.android.support:design:25.1.0' </div><div> compile 'com.android.support:support-v4:25.3.1'</div><div> compile 'com.android.support:preference-v14:25.3.1'</div>
子主题
第三方
LeakCanary
<div> debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.2'</div><div> releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.2'</div>
底部tab
<div>compile 'com.shizhefei:ViewPagerIndicator:1.1.6'</div><div>由于用到了v4和recyclerview所以也要导入他们</div><div>compile 'com.android.support:support-v4:23.4.0'</div><div>compile 'com.android.support:recyclerview-v7:23.4.0'</div>
混淆
mapping
路径
\\172.16.7.69\temp\BLauncherReleaseMapping
配置
签名
build.gradle
local.properties
本地Maven
类库端配置
build.gradle
配置完后,点击Gradle的选项,找到该Moudle
使用端配置
版本兼容
com.android.tools.build:gradle:3.2.1<br>
distributions/gradle-4.6-all.zip
com.google.protobuf:protoc:3.0.0-alpha-3 15/05
4.10-all 18/08Aug/27
4.10.1-all 18/09Sep/
distributions/gradle-5.1.1-all.zip
gradle:3.1.2
buildTools:27.0.3以上
gradle-4.4-all 以上
gradle:3.5.2
gradle-5.4.1-all.zip
6.0-all
kotlin 1.3.50
<br> ext.kotlin_version = "1.3.50"
//手动添加以下两行<br> classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"<br> classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
界面控件API,功能
EditText
textColorHighlight
如果不需要改变小水滴的图片,只是希望改变小水滴的颜色,那么只需要改变android:colorControlActivated的颜色值即可<br>亲测无效
默认使用主题色中的colorAccent
textSelectHandle(选择文本的小水滴)
android:textSelectHandle 中间的选择器图标<br>android:textSelectHandleLeft 左边的选择器图标<br>android:textSelectHandleRight 右边的选择器图标
android:textSelectHandle="@color/blue_cursor_color" <br>
设置颜色变成空白
android:textColorHighlight
文字选中背景
android:textCursorDrawable="@drawable/cursor_color"
光标图片资源,drawable可以为纯颜色
drawable/cursor_color
点击全选
功能
输入限制
不可编辑状态
监听事件
小数点2位
ListView
默认选择的位置
长按事件
在ScrlloView中
PinnedHeaderListView
PinnedHeaderExpandableListView
下拉刷新
安卓无ActionBar
TextView
加下划线
半圆背景
粗体
多行省略
字体颜色区分
跑马灯
按下改变文字颜色
selector<br>drawable/bg_button_text_color.xml
android:textColor="@drawable/bg_button_text_color" <br>
其他控件及效果
弹窗
DialogFragment
showEditDialog();
class
listener
CustomDialog
GridView
GridView 元素间距设定
选择(选中)控件
RadioGroup
RadioButton
XML
Selector
Java
Spinner
CheckBox
选中事件
ScrollView
焦点移动最顶端
系统控件
NavigationBar和StatusBar
设置颜色
getWindow().setNavigationBarColor(Color.BLUE);
0 条评论
下一页