一、TabLayout
1.1 TabLayout常见XML属性
属性 |
说明 |
app:tabIndicatorColor |
tab滚动指示横线颜色 |
app:tabSelectedTextColor |
被选中tab子项的文本颜色 |
app:tabTextColor |
tab子项默认的文本颜色 |
1.2 Java调用
1
2
3
| TabLayout mTabLayout = (TabLayout) findViewById(R.id.tabLayout);
TabLayout.Tab mContactsTab = mTabLayout.newTab().setText("Contacts");
mTabLayout.addTab(mContactsTab, true); // 添加Tab且默认选中
|
1.3 滚动模式
1
| mTabLayout.setTabMode(TabLayout.MODE_FIXED);
|
一共两种模式:
- TabLayout.MODE_FIXED (默认)
- TabLayout.MODE_SCROLLABLE
1.4 添加监听
1
2
3
4
5
6
7
8
9
10
| mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {}
@Override
public void onTabUnselected(TabLayout.Tab tab) {}
@Override
public void onTabReselected(TabLayout.Tab tab) {}
});
|
1.5 TabGravity设置
只在TabMode为TabLayout.MODE_FIXED
时才会生效。
GRAVITY_FILL
占满TabLayout
空间,GRAVITY_CENTER
居中Wrap_Content
。
1
2
| mTabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
mTabLayout.setTabGravity(TabLayout.GRAVITY_CENTER);
|
在mPagerAdapter
添加页面,绑定到mViewPager
上。
1
2
3
4
5
6
7
8
9
10
11
| private ViewPagerAdapter mPagerAdapter;
private ViewPager mViewPager = (ViewPager) findViewById(R.id.viewPager);
private TabLayout mTabLayout = (TabLayout) findViewById(R.id.tabLayout);
mPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
mPagerAdapter.addTab(new MainFragment(), "所有书签");
mPagerAdapter.addTab(new ReadingFragment(), "正在阅读");
mPagerAdapter.addTab(new ReadFragment(), "完成阅读");
mViewPager.setAdapter(mPagerAdapter);
mTabLayout.setupWithViewPager(viewPager, false);
|
使用的ViewPagerAdaptor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| public class ViewPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> fragments = new ArrayList<>();
private final List<String> titles = new ArrayList<>();
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
public void addTab(Fragment fragment, String title) {
fragments.add(fragment);
titles.add(title);
}
public Fragment getFragments(int position) {
return fragments.get(position);
}
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments.size();
}
@Override
public CharSequence getPageTitle(int position) {
return titles.get(position);
}
}
|
二、DrawerLayout
2.1 DrawerLayout常用功能
获取抽屉状态和控制抽屉开闭
1
2
3
4
| mDrawerLayout.isDrawerOpen(GravityCompat.START); // boolean, 抽屉是否展开
mDrawerLayout.closeDrawer(GravityCompat.START); // void, 收起抽屉
mDrawerLayout.openDrawer(GravityCompat.START); // void, 打开抽屉
|
用法示例
1
2
3
4
5
6
| private DrawerLayout mDrawerLayout; // 声明控件
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); // 初始化控件
if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
mDrawerLayout.closeDrawer(GravityCompat.START);
}
|
2.2 DrawerLayout与startActivity冲突
参考自 Optimizing drawer and activity launching speed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| class SmoothDrawerToggle extends ActionBarDrawerToggle {
private Runnable runnable;
SmoothDrawerToggle(Activity activity, DrawerLayout drawerLayout, Toolbar toolbar) {
super(activity, drawerLayout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
}
@Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
invalidateOptionsMenu();
}
@Override
public void onDrawerClosed(View view) {
super.onDrawerClosed(view);
invalidateOptionsMenu();
}
@Override
public void onDrawerStateChanged(int newState) {
super.onDrawerStateChanged(newState);
if (runnable != null && newState == DrawerLayout.STATE_IDLE) {
runnable.run();
runnable = null;
}
}
void runWhenIdle(Runnable runnable) {
this.runnable = runnable;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| private SmoothDrawerToggle mToggle;
private DrawerLayout drawerLayout;
private NavigationView navView;
private Toolbar toolBar;
// 在onCreate()中初始化
navView.setNavigationItemSelectedListener(this);
// NavigationView.OnNavigationItemSelectedListener
mToggle = new SmoothDrawerToggle(this, drawerLayout, toolBar);
// Activity.Context, 设置该抽屉xml的ID, toolBar用于抽屉开闭图标
drawerLayout.addDrawerListener(mToggle);
// 给抽屉加入自定义的Listener
mToggle.syncState();
|
2.3 箭头颜色
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| <resources>
<!-- DrawerArrowStyle -->
<style name="DrawerArrowStyle" parent="Widget.AppCompat.DrawerArrowToggle">
<item name="spinBars">true</item>
<item name="color">#FFFFFF</item>
</style>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="drawerArrowStyle">@style/DrawerArrowStyle</item>
</style>
</resources>
|
三、SplashActivity
闪屏页代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
| public class SplashActivity extends Activity {
public static final String SHOW_SPLASH = "SHOW_SPLASH";
private static final int START_ACTIVITY = 0x01;
private static final int SPLASH_DURATION = 3000;
private static final int NO_SPLASH_DURATION = 0;
private SplashHandler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
mHandler = new SplashHandler(Looper.getMainLooper());
boolean startSplash = SharedPrefsUtil.getBoolean(SHOW_SPLASH, false, this);
if (startSplash) {
mHandler.sendEmptyMessageDelayed(START_ACTIVITY, NO_SPLASH_DURATION);
} else {
mHandler.sendEmptyMessageDelayed(START_ACTIVITY, SPLASH_DURATION);
}
}
class SplashHandler extends Handler {
SplashHandler(Looper mLooper) {
super(mLooper);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Intent intent = new Intent(SplashActivity.this, MainActivity.class);
startActivity(intent);
//切换Activity的过程中渐变效果
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
SplashActivity.this.finish();
}
}
// 单击提早关闭SplashActivity,选用
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mHandler.removeMessages(START_ACTIVITY);
mHandler.sendEmptyMessage(START_ACTIVITY);
}
return true;
}
// 屏蔽返回按键
@Override
public void onBackPressed() {
}
}
|
四、Utils
4.1 ToastUtil
避免多次连续使用Toast时一直显示Toast,为了避免内存泄漏,使用的Context请调用ApplicationContext.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| public final class ToastUtil {
private static Toast mToast;
private static String mLastMsg;
private static long mFirstTime = 0;
private static long mSecondTime = 0;
public static void makeText(Context context, String msg, int duration) {
if (mToast == null) {
mToast = Toast.makeText(context, msg, duration);
mToast.show();
mFirstTime = System.currentTimeMillis();
} else {
mSecondTime = System.currentTimeMillis();
if (msg.equals(mLastMsg)) {
if (mSecondTime - mFirstTime > 2500) {
mToast.show();
}
} else {
mLastMsg = msg;
mToast.setText(msg);
mToast.show();
}
}
mFirstTime = mSecondTime;
}
public static void makeText(Context context, int text, int duration) {
makeText(context, context.getString(text), duration);
}
public static void cancel(Context context) {
if (mToast != null) {
mToast.cancel();
}
}
}
|
4.2 DoubleClickUtil
双击返回键退出应用
1
2
3
4
5
6
7
8
9
10
11
12
| public class ClickHelper {
private static long mLastClick = 0L;
private static final int THRESHOLD = 2000;
public static boolean check() {
long now = System.currentTimeMillis();
boolean exit = now - mLastClick < THRESHOLD;
mLastClick = now;
return exit;
}
}
|
4.3 GlideCacheUtil
提供Glide磁盘缓存占用大小和清理功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
| public class GlideCacheUtil {
/**
* 公开的缓存清理方法
*
* @param context Activity context
*/
public static void clearAll(Context context) {
clearDisk(context);
clearMemory(context);
String cacheDir = context.getExternalCacheDir() + ExternalCacheDiskCacheFactory.DEFAULT_DISK_CACHE_DIR;
clearFile(cacheDir, true);
}
/**
* 获取Glide缓存大小的字符串
*
* @param context Activity context
* @return 包含Glide缓存大小的字符串,如"324.49KB"
*/
public static String getCacheSize(Context context) {
File file = new File(context.getCacheDir() + "/" + InternalCacheDiskCacheFactory.DEFAULT_DISK_CACHE_DIR);
double folderSize = getFolderSize(file);
return formatSize(folderSize);
}
/**
* 清除Glide磁盘缓存内容,必须运行在子线程,避免阻塞主线程
*
* @param context Activity context
*/
private static void clearDisk(final Context context) {
if (Looper.myLooper() == Looper.getMainLooper()) {
new Thread(new Runnable() {
@Override
public void run() {
Glide.get(context).clearDiskCache();
}
}).start();
} else {
Glide.get(context).clearDiskCache();
}
}
/**
* 清除Glide内存的缓存内容,必须运行在主线程
*
* @param context Activity context
*/
private static void clearMemory(final Context context) {
if (Looper.myLooper() == Looper.getMainLooper()) {
Glide.get(context).clearMemory();
}
}
/**
* 统计文件夹大小
*
* @param file 文件夹路径
* @return 返回文件夹大小,单位字节
*/
private static double getFolderSize(File file) {
File[] list = file.listFiles();
if (list == null) {
return 0;
} else {
double size = 0;
for (File f : list) {
size += (f.isDirectory() ? getFolderSize(f) : f.length());
}
return size;
}
}
/**
* 清除缓存文件
*
* @param filePath 缓存路径
* @param deleteThisPath 是否删除缓存根目录
* @return 删除结果布尔标志
*/
private static boolean clearFile(String filePath, boolean deleteThisPath) {
boolean flag = false;
if (!Utils.isEmpty(filePath)) {
try {
File file = new File(filePath);
if (file.isDirectory()) {
File files[] = file.listFiles();
for (File f : files) {
clearFile(f.getAbsolutePath(), deleteThisPath);
}
}
if (deleteThisPath) {
if (!file.isDirectory()) {
flag = file.delete();
} else {
if (file.listFiles().length == 0) {
flag = file.delete();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
return flag;
}
/**
* 返回表示Glide缓存大小的字符串,如"324.49KB"、"3.72MB"
*
* @param size 缓存文件夹的大小,单位为字节
* @return 缓存大小,自适应单位KB/MB
*/
private static String formatSize(double size) {
size /= 1024; // from B to KB
if (size >= 1024) {
BigDecimal sizeMB = new BigDecimal(Double.toString(size / 1024));
return sizeMB.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "MB";
} else {
BigDecimal sizeKB = new BigDecimal(Double.toString(size));
return sizeKB.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "KB";
}
}
}
|
五、获取库图片路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| //调用的Intent
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(intent, FOR_RESULT_SUCCESS);
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == FOR_RESULT_SUCCESS && data != null) {
Uri uri = data.getData();
String[] project = {MediaStore.Images.Media.DATA};
CursorLoader loader = new CursorLoader(this, uri, project, null, null, null);
Cursor cursor = loader.loadInBackground();
int index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
String path = cursor.getString(index);
}
}
|
六、ProgressBar 进度条
进度条布局代码
1
2
3
4
5
6
7
8
9
| <ProgressBar
android:id="@+id/pb_progressbar"
style="@style/StyleProgressBarMini"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_margin="30dp"
android:background="@drawable/shape_progressbar_bg"
android:max="1000"
android:progress="768" />
|
@style/StyleProgressBarMini
1
2
3
4
5
6
7
| <style name="StyleProgressBarMini" parent="@android:style/Widget.ProgressBar.Horizontal">
<item name="android:maxHeight">50dp</item>
<item name="android:minHeight">10dp</item>
<item name="android:indeterminateOnly">false</item>
<item name="android:indeterminateDrawable">@android:drawable/progress_indeterminate_horizontal</item>
<item name="android:progressDrawable">@drawable/shape_progressbar_mini</item>
</style>
|
@drawable/shape_progressbar_mini
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| <?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- 背景 -->
<item android:id="@android:id/background">
<shape>
<corners android:radius="5dip" />
<gradient
android:angle="270"
android:centerY="0.75"
android:endColor="#FFFFFF"
android:startColor="#FFFFFF" />
</shape>
</item>
<item android:id="@android:id/secondaryProgress">
<clip>
<shape>
<corners android:radius="0dip" />
<gradient
android:angle="270"
android:centerY="0.75"
android:endColor="#df0024"
android:startColor="#df0024" />
</shape>
</clip>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<corners android:radius="5dip" />
<gradient
android:angle="270"
android:centerY="0.75"
android:endColor="#de42ec"
android:startColor="#de42ec" />
</shape>
</clip>
</item>
</layer-list>
|
@drawable/shape_progressbar_bg
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| <?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<!-- 边框填充的颜色 -->
<solid android:color="#cecece" />
<!-- 设置进度条的四个角为弧形的半径 -->
<corners android:radius="90dp" />
<!-- padding:边界的间隔 -->
<padding
android:bottom="1dp"
android:left="1dp"
android:right="1dp"
android:top="1dp" />
</shape>
|
1
2
| 'supportInvalidateOptionsMenu(): Unit' is deprecated. Overrides deprecated member in 'android.support.v4.app.FragmentActivity'. Deprecated in Java
:projectName:javaPreCompileDebug
|
修复方法:
使用以下方法替换supportInvalidateOptionsMenu()
1
| invalidateOptionsMenu()
|
Android Studio提示使用android.support.media.ExifInterface
替换android.media.ExifInterface
,但是直接修改没法找到对应包,需要导入:compile "com.android.support:exifinterface:27.0.2"