項(xiàng)目總結(jié)
最近在公司做了一個(gè)非常輕量級(jí)別的app罢荡,不過(guò)里面還是有一些知識(shí)點(diǎn)逛揩,是查了資料之后才會(huì)的辩稽,現(xiàn)在app基本做完了,整體總結(jié)一下患整。
1.獲取當(dāng)前app的一些基礎(chǔ)信息:
public static final boolean DEBUG = BuildConfig.DEBUG;
//以下是能獲取到的信息
public static final boolean DEBUG = Boolean.parseBoolean("true");
public static final String APPLICATION_ID = "com.fuyizhulao.daily_service";
public static final String BUILD_TYPE = "debug";
public static final String FLAVOR = "";
public static final int VERSION_CODE = 1;
public static final String VERSION_NAME = "1.0.0";
2.高德地圖(具體用法見(jiàn)官網(wǎng)吧各谚,文檔很詳細(xì)了)
3.個(gè)推(一個(gè)推送服務(wù),很好用赴穗,同時(shí)它的別名機(jī)制也很人性化膀息,省著后臺(tái)再維護(hù)一套映射了)
4.Bugly(騰訊的一款崩潰統(tǒng)計(jì)潜支,異常上報(bào)的SDK,非常好配置埠对,非常的好用)
5.配置回調(diào)類(lèi)的時(shí)候裁替,可以配制成泛型的,非常的方便稍计、靈活
public interface NetWorkListener<T> {
void onSuccess(T netWorkModel);
void onFail(T netWorkModel);
}
6.在Fragment中想和Activity通信,可以通過(guò)EventBus進(jìn)行通信净刮,但是也不要過(guò)分依賴(lài)這個(gè)吧淹父,因?yàn)楫?dāng)過(guò)分依賴(lài)之后,整個(gè)代碼的邏輯會(huì)變得異常的復(fù)雜困介,同時(shí)發(fā)生異常之后蘸际,也是非常不好檢查的粮彤。
7.在app中無(wú)論一個(gè)網(wǎng)絡(luò)接口出現(xiàn)過(guò)幾次姜骡,最好還是統(tǒng)一的封裝起來(lái)會(huì)比較方便一點(diǎn)圈澈。
8.將Model專(zhuān)程Json字符串可以用Gson:
new Gson().toJson(new GetOrderList(
Integer.valueOf(MyApplication.id)
, state
, pageNum
, pageSize))
9.得到相機(jī)的View和控制閃光燈:
public class CameraView extends SurfaceView implements SurfaceHolder.Callback, Camera.PreviewCallback {
private Camera mCamera;
private int mPreviewRotation = 90;
private int mCamId = Camera.CameraInfo.CAMERA_FACING_BACK;
private PreviewCallback mPrevCb;
private byte[] mYuvPreviewFrame;
private int previewWidth;
private int previewHeight;
private Camera.Parameters params;
public interface PreviewCallback {
void onGetYuvFrame(byte[] data);
}
public CameraView(Context context) {
this(context, null);
}
public CameraView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setPreviewRotation(int rotation) {
mPreviewRotation = rotation;
}
public void setCameraId(int id) {
mCamId = id;
}
public int getCameraId() {
return mCamId;
}
public void setPreviewCallback(PreviewCallback cb) {
mPrevCb = cb;
getHolder().addCallback(this);
}
public void setPreviewResolution(int width, int height) {
previewWidth = width;
previewHeight = height;
}
public boolean startCamera() {
if (mCamera != null) {
return false;
}
if (mCamId > (Camera.getNumberOfCameras() - 1) || mCamId < 0) {
return false;
}
mCamera = Camera.open(mCamId);
params = mCamera.getParameters();
Camera.Size size = mCamera.new Size(previewWidth, previewHeight);
mYuvPreviewFrame = new byte[previewWidth * previewHeight * 3 / 2];
params.setPreviewSize(previewWidth, previewHeight);
params.setPreviewFormat(ImageFormat.NV21);
List<String> supportedFocusModes = params.getSupportedFocusModes();
if (!supportedFocusModes.isEmpty()) {
if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
}
}
mCamera.setParameters(params);
mCamera.setDisplayOrientation(mPreviewRotation);
mCamera.addCallbackBuffer(mYuvPreviewFrame);
mCamera.setPreviewCallbackWithBuffer(this);
try {
mCamera.setPreviewDisplay(getHolder());
} catch (IOException e) {
e.printStackTrace();
}
mCamera.startPreview();
return true;
}
public void stopCamera() {
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
mPrevCb.onGetYuvFrame(data);
camera.addCallbackBuffer(mYuvPreviewFrame);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceCreated(SurfaceHolder arg0) {
if (mCamera != null) {
try {
mCamera.setPreviewDisplay(getHolder());
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
}
public void isOpenLight(boolean isOpen) {
if (isOpen) {
params.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
mCamera.setParameters(params);
} else {
params.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
mCamera.setParameters(params);
}
}
}
10.Notification
NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
mBuilder.setContentTitle(title)//設(shè)置通知欄標(biāo)題
.setContentIntent(getDefalutIntent(Notification.FLAG_AUTO_CANCEL)) //設(shè)置通知欄點(diǎn)擊意圖
.setNumber(number) //設(shè)置通知集合的數(shù)量
.setTicker(title) //通知首次出現(xiàn)在通知欄喷橙,帶上升動(dòng)畫(huà)效果的
.setWhen(System.currentTimeMillis())//通知產(chǎn)生的時(shí)間,會(huì)在通知信息里顯示饥臂,一般是系統(tǒng)獲取到的時(shí)間
.setPriority(Notification.PRIORITY_DEFAULT) //設(shè)置該通知優(yōu)先級(jí)
.setAutoCancel(true)//設(shè)置這個(gè)標(biāo)志當(dāng)用戶(hù)單擊面板就可以讓通知將自動(dòng)取消
.setOngoing(false)//ture似踱,設(shè)置他為一個(gè)正在進(jìn)行的通知核芽。他們通常是用來(lái)表示一個(gè)后臺(tái)任務(wù),用戶(hù)積極參與(如播放音樂(lè))或以某種方式正在等待,因此占用設(shè)備(如一個(gè)文件下載,同步操作,主動(dòng)網(wǎng)絡(luò)連接)
.setDefaults(Notification.DEFAULT_VIBRATE)//向通知添加聲音、閃燈和振動(dòng)效果的最簡(jiǎn)單驰坊、最一致的方式是使用當(dāng)前的用戶(hù)默認(rèn)設(shè)置哮独,使用defaults屬性皮璧,可以組合
//Notification.DEFAULT_ALL Notification.DEFAULT_SOUND 添加聲音 // requires VIBRATE permission
.setSmallIcon(R.mipmap.ic_launcher);//設(shè)置通知小ICON
mNotificationManager.notify(1, mBuilder.build());
11.沉浸式狀態(tài)欄
這是一個(gè)第三方庫(kù),用起來(lái)挺方便的睹限,有空應(yīng)該擼一遍它的源碼
compile 'com.readystatesoftware.systembartint:systembartint:1.0.3'
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
//透明狀態(tài)欄 getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
//透明導(dǎo)航欄getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
SystemBarTintManager tintManager = new SystemBarTintManager(this);
// 激活狀態(tài)欄
tintManager.setStatusBarTintEnabled(true);
// enable navigation bar tint 激活導(dǎo)航欄
//tintManager.setNavigationBarTintEnabled(true);
//設(shè)置系統(tǒng)欄設(shè)置顏色
//tintManager.setTintColor(R.color.red);
//給狀態(tài)欄設(shè)置顏色
tintManager.setStatusBarTintResource(R.color.normal_blue);
//Apply the specified drawable or color resource to the system navigation bar.
//給導(dǎo)航欄設(shè)置資源
//tintManager.setNavigationBarTintResource(R.color.normal_blue);
}
android:fitsSystemWindows="true"
android:clipToPadding="true"
12.利用反射動(dòng)態(tài)調(diào)節(jié)tablayout的長(zhǎng)度
public void setIndicator(TabLayout tabs, int leftDip, int rightDip) {
Class<?> tabLayout = tabs.getClass();
Field tabStrip = null;
try {
tabStrip = tabLayout.getDeclaredField("mTabStrip");
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
tabStrip.setAccessible(true);
LinearLayout llTab = null;
try {
llTab = (LinearLayout) tabStrip.get(tabs);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
int left = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, leftDip, Resources.getSystem().getDisplayMetrics());
int right = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, rightDip, Resources.getSystem().getDisplayMetrics());
for (int i = 0; i < llTab.getChildCount(); i++) {
View child = llTab.getChildAt(i);
child.setPadding(0, 0, 0, 0);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 1);
params.leftMargin = left;
params.rightMargin = right;
child.setLayoutParams(params);
child.invalidate();
}
}
13.驗(yàn)證碼倒計(jì)時(shí)
mTimeCount = new TimeCount(60000, 1000);
mTimeCount.start();
class TimeCount extends CountDownTimer {
public TimeCount(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}
@Override
public void onFinish() {// 計(jì)時(shí)完畢
mGetCode.setText("獲取驗(yàn)證碼");
mGetCode.setClickable(true);
}
@Override
public void onTick(long millisUntilFinished) {// 計(jì)時(shí)過(guò)程
mGetCode.setClickable(false);//防止重復(fù)點(diǎn)擊
mGetCode.setText(String.valueOf(millisUntilFinished / 1000) + "秒后重發(fā)");
}
}
14.downloadManager工具類(lèi)
public class DownloadUtils {
private DownloadManager mDownloadManager;
private Context mContext;
private long downloadId;
private String apkName;
public DownloadUtils(Context context) {
mContext = context;
}
public void download(String url, String name) {
final String packageName = "com.android.providers.downloads";
int state = mContext.getPackageManager().getApplicationEnabledSetting(packageName);
//檢測(cè)下載管理器是否被禁用
if (state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
|| state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER
|| state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
AlertDialog.Builder builder = new AlertDialog.Builder(mContext).setTitle("溫馨提示").setMessage
("系統(tǒng)下載管理器被禁止别洪,需手動(dòng)打開(kāi)").setPositiveButton("確定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
try {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse("package:" + packageName));
mContext.startActivity(intent);
} catch (ActivityNotFoundException e) {
Intent intent = new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS);
mContext.startActivity(intent);
}
}
}).setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
builder.create().show();
} else {
//正常下載流程
apkName = name;
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setAllowedOverRoaming(false);
//通知欄顯示
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setTitle("test");
request.setDescription("正在下載中...");
request.setVisibleInDownloadsUi(true);
//設(shè)置下載的路徑
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, apkName);
//獲取DownloadManager
mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
downloadId = mDownloadManager.enqueue(request);
mContext.registerReceiver(mReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
checkStatus();
}
};
/**
* 檢查下載狀態(tài)
*/
private void checkStatus() {
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(downloadId);
Cursor cursor = mDownloadManager.query(query);
if (cursor.moveToFirst()) {
int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
switch (status) {
//下載暫停
case DownloadManager.STATUS_PAUSED:
break;
//下載延遲
case DownloadManager.STATUS_PENDING:
break;
//正在下載
case DownloadManager.STATUS_RUNNING:
break;
//下載完成
case DownloadManager.STATUS_SUCCESSFUL:
installAPK();
break;
//下載失敗
case DownloadManager.STATUS_FAILED:
Toast.makeText(mContext, "下載失敗", Toast.LENGTH_SHORT).show();
break;
}
}
cursor.close();
}
/**
* 7.0兼容
*/
private void installAPK() {
File apkFile =
new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), apkName);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Uri apkUri = FileProvider.getUriForFile(mContext, mContext.getPackageName() + ".provider", apkFile);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
}
mContext.startActivity(intent);
}
}
15.還有一些簡(jiǎn)單的工具類(lèi):
public class Util {
/**
* 將時(shí)間戳轉(zhuǎn)換為時(shí)間
*/
public static String stampToDate(String s) {
String res;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
long lt = new Long(s);
Date date = new Date(lt);
res = simpleDateFormat.format(date);
return res;
}
/**
* 這個(gè)工具類(lèi)是為了展示出首頁(yè)那種持續(xù)的效果
* 例如:12月21日 09:00 - 10:00
*/
public static String stampAndDurationToDate(long serviceTime, long duration) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MM月dd日 HH:mm");
Date date = new Date(serviceTime);
String myData = simpleDateFormat.format(date);
Date date2 = new Date(serviceTime + duration * 60 * 1000);
String myData2 = simpleDateFormat.format(date2);
return myData + "-" + myData2.split(" ")[1];
}
/**
* 這個(gè)工具類(lèi)是為了將時(shí)間戳轉(zhuǎn)換成訂單上面的時(shí)間
* 例如:2016-12-21 09:00-10:00
*/
public static String stampAndDurationToDateForOrder(long serviceTime, long duration) {
String date1 = stampToDate(String.valueOf(serviceTime));
String date2 = stampToDate(String.valueOf(serviceTime + duration * 60 * 1000));
return date1 + "-" + date2.split(" ")[1];
}
/**
* bitmap轉(zhuǎn)file
*/
public static File saveBitmapFile(Bitmap bitmap) {
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/img.png");//將要保存圖片的路徑
try {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);
bos.flush();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
return file;
}
}
16.cardView的點(diǎn)擊效果
android:foreground="@drawable/card_view_select"
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/normal_white" android:state_pressed="false"> </item>
<item android:drawable="@color/press_white" android:state_pressed="true"> </item>
</selector>