Android开发细节——开发实战过程中遇到的细节问题与解决方案汇总
2017-06-07 12:23
776 查看
获取系统时间的24小时制与12小时制
最近在做项目的时候发生了一点错误,服务器端是24小时制的时间,而本地数据库则是12小时制的时间1、获取24小时制的时间
public static String showDate() { SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String date = sDateFormat.format(new Date()); return date; }
2、获取12小时制的时间
public static String showDate() { SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); String date = sDateFormat.format(new Date()); return date; }
两者区别就在于HH和hh,可以看下文档的其他示例
ViewPager中的Fragment在切换时不重新加载
最近在做项目的时候发生了一点错误,在ViewPager中的Fragment中切换到第三页重新切回第一页时,会重新加载第一页,显得页面一直在加载,降低了用户体验,因为viewPager会事先加载好当前页的前后两页,也就是到了第三页的时候,第一页已经被销毁了,回到第二页的时候会重新创建解决方法
//默认是1 mViewPager.setOffscreenPageLimit(3);
优化购物车,选中物品时价钱相加减的精确运算
做到商品购物车模块的时候,发现价钱的加减并不能单纯的使用+、-来实现,由于我们的价格都是double类型的,如10.24元,相互加减的时候会出现20.45555555的情况,所以我们需要使用到API中BigDecimal这个类进行包装,然后运算代码如下:
public void selectSingle() { //创建BigDecimal对象 BigDecimal bj1 = new BigDecimal(Double.toString(money1)); BigDecimal bj2 = new BigDecimal(Double.toString(money2)); if (selected_Id.contains(shop.get_id())) {//相减 sum_money = bj1.subtract(bj2).doubleValue(); } else {//相加 sum_money = bj1.add(bj2).doubleValue(); } }
效果如下:可以看到价钱已经是正常的加减了
启动模式的细节
情景发生:当我们应用程序按Home键返回桌面时,再次点击应用程序不能恢复到离开时的Activity解决方案:只要该栈中之前的任何一个Activity在manifest文件中定义了启动模式为singleTask,那么再次点击应用时会启动第一个Activity,只要去除之前Activity中singleTask属性就能恢复回离开时的Activity
通过广播监听网络的变化清况
通常在使用软件的时候会出现网路变化的情况,比如Wifi断线导致使用流量上网,这个时候作为我们的软件就必须通知用户在使用流量上网。首先,Manifests中注册网络变化情况的广播<!-- 广播 --> <receiver android:name=".Receiver.NetReceiver"> <intent-filter> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> </intent-filter> </receiver>
接着,创建该Receiver,根据网络的变化情况进行相对应的提醒。这里当用户打开或关闭Wifi和移动数据时,该广播可以收到
public class NetReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { //网络广播接收者 if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) { ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeInfo = connectivityManager.getActiveNetworkInfo(); NetworkInfo netInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); NetworkInfo wifiInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); if (activeInfo != null) { //网络可用 if (activeInfo.getState().equals(NetworkInfo.State.CONNECTED)) { //判断移动数据 if (netInfo.getState().equals(NetworkInfo.State.CONNECTED)) { Toast.makeText(context, "您正在使用移动数据", Toast.LENGTH_SHORT).show(); } //判斷Wifi數據 if (wifiInfo.getState().equals(NetworkInfo.State.CONNECTED)) { Toast.makeText(context, "您正在使用Wifi数据", Toast.LENGTH_SHORT).show(); } } else { Toast.makeText(context, "请检查网络是否已联网", Toast.LENGTH_SHORT).show(); } } } } }
优化Gradle,编译时间从33.8秒降到4.5秒
在项目的gradle中添加以下语句tasks.whenTaskAdded { task -> if (task.name.contains("lint") || task.name.equals("clean") || task.name.contains("Aidl") || task.name.contains("mockableAndroidJar") || task.name.contains("UnitTest") || task.name.contains("AndroidTest") || task.name.contains("Ndk") || task.name.contains("Jni") ) { task.enabled = false; } }
Fragment的懒加载基类
在项目中添加该基类,新的Fragment继承该BaseFragment就可以轻松实现Fragment的懒加载public abstract class BaseFragment extends Fragment implements View.OnClickListener { private boolean isPrepared; private boolean isVisible; public abstract View initViews(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState); public abstract void initData(); public abstract void initListener(); public abstract void processClick(View v); @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (getUserVisibleHint()) { isVisible = true; lazyLoad(); } else { isVisible = false; } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return initViews(inflater, container, savedInstanceState); } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); isPrepared = true; lazyLoad(); } /** * 懒加载 */ private void lazyLoad() { if (!isVisible || !isPrepared) { return; } //加载数据 initData(); initListener(); } @Override public void onClick(View v) { processClick(v); } }
优化错误,不让错误导致程序崩溃
在项目中,常常会因为list.get(0)获取没有数据而导致程序崩溃,这个时候应该把程序try-catch起来,让程序报错但不崩溃,比如public Drawable getDrawable(String name) { try { return mResources.getDrawable(mResources.getIdentifier(name, "drawable", mPkgName)); } catch (Exception e) { e.printStackTrace(); return null; } }
Context的应用场景
解决PhotoView在ViewPager中多点触摸时,报错崩溃的方法
使用PhotoView在ViewPager中展示出多图操作,如果操作过于频繁,那么下面这个错误java.lang.IllegalArgumentException: pointerIndex out of range
其解决方法就是在PhotoView所在的Activity中添加下面的处理即可
@Override public boolean dispatchTouchEvent(MotionEvent ev) { try { return super.dispatchTouchEvent(ev); } catch (IllegalArgumentException ex) { ex.printStackTrace(); } return false; }
使用Html.fromhtml显示图片
在项目开发中,后台常常会使用这样的编辑器来编辑图文返回在android端是一串html字符串,这个时候单单靠Html.fromhtml是显示不出来图片的,图片将会被小方格替代,下面是我已经处理好的一个类,该类只要Html.fromhtml识别到图片就会回调该类的getDrawable()方法。该类兼容网络图片和服务器图片,兼容以下两种格式
服务器图片url:/upload/2017/05/04/590aaae1b0d4a.png
网络图片url:https://p.ssl.qhimg.com/t0140ba2595bb1b8c4a.png
/** * @author 许英俊 2017/6/7 */ public class ImageGetterImpl implements Html.ImageGetter { private int width, height; private TextView tv; private String html; private File file; public ImageGetterImpl(TextView tv, String html, int width, int height) { this.tv = tv; this.html = html; this.width = width - 80; this.height = height; } @Override public Drawable getDrawable(String source) { Drawable drawable = null; // 区分网络图片和自己服务器图片,如果是服务器图片,则加个根目录 if (!source.startsWith("http")) { source = RequestCenter.ROOT_URL + source; } //获取图片后缀名作为文件名 String[] fileName = source.split("/"); file = new File(Environment.getExternalStorageDirectory(), fileName[fileName.length - 1]); // 判断是否以http开头 if (source.startsWith("http")) { // 判断路径是否存在 if (file.exists()) { // 存在即获取drawable drawable = Drawable.createFromPath(file.getAbsolutePath()); // 根据屏幕的宽高比等于图片的宽高比 height = (width) * drawable.getIntrinsicHeight() / drawable.getIntrinsicWidth(); drawable.setBounds(0, 0, width, height); } else { // 不存在即开启异步任务加载网络图片 AsyncLoadNetworkPic networkPic = new AsyncLoadNetworkPic(); networkPic.execute(source); } } return drawable; } public class AsyncLoadNetworkPic extends AsyncTask<String, Integer, Void> { @Override protected Void doInBackground(String... params) { // 加载网络图片 loadNetPic(params); return null; } @Override protected void onPostExecute(Void result) { super.onPostExecute(result); // 当执行完成后再次为其设置一次 tv.setText(Html.fromHtml(html, new ImageGetterImpl(tv, html, width, height), null)); } /** * 加载网络图片 */ private void loadNetPic(String... params) { String path = params[0]; InputStream in = null; FileOutputStream out = null; try { URL url = new URL(path); HttpURLConnection connUrl = (HttpURLConnection) url.openConnection(); connUrl.setConnectTimeout(10000); connUrl.setRequestMethod("GET"); if (connUrl.getResponseCode() == 200) { in = connUrl.getInputStream(); out = new FileOutputStream(file); byte[] buffer = new byte[1024]; int len; while ((len = in.read(buffer)) != -1) { out.write(buffer, 0, len); } } else { Log.e("TAG", connUrl.getResponseCode() + ""); } } catch (Exception e) { e.printStackTrace(); } finally { if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if (out != null) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } } } }
对应的在安卓端使用三个参数的方法进行解析html字符串,其中DensityUtils两个方法表示获取屏幕的宽和高
tv_content.setText(Html.fromHtml(data.getContent(), new ImageGetterImpl(tv_content, data.getContent(), DensityUtils.getDisplayWidth(this), DensityUtils.getDisplayHeight(this)), null));
在手机的显示效果如下,中间隔那么多行请忽略
网络缓存
打开缓存try { File httpCacheDir = new File(context.getCacheDir(), "http"); long httpCacheSize = 10 * 1024 * 1024; // 10 MiB HttpResponseCache.install(httpCacheDir, httpCacheSize); } catch (IOException e) { Log.i(TAG, "HTTP response cache installation failed:" + e); }
清除缓存
HttpResponseCache cache = HttpResponseCache.getInstalled(); if (cache != null) { cache.delete(); }
如果对于某个请求我不想用cache,则可以使用下面代码
connection.addRequestProperty("Cache-Control", "no-cache");
我想用cache但是又怕服务器的数据已经发生改变了,则可以使用下面代码
connection.addRequestProperty("Cache-Control", "max-age=0");
由于HttpResponseCache的API是Android4.0提供的,兼容Android4.0以下版本
try { File httpCacheDir = new File(context.getCacheDir(), "http"); long httpCacheSize = 10 * 1024 * 1024; // 10 MiB Class.forName("android.net.http.HttpResponseCache") .getMethod("install", File.class, long.class) .invoke(null, httpCacheDir, httpCacheSize); } catch (Exception httpResponseCacheNotAvailable) { } }
屏幕像素的适配
一般我们开发的app不会去适配小屏幕的手机,普遍适配1080 x 1920的手机,通过图中可以看出其density密度为480,那么通过dp与px的运算公式(1dp x 像素密度 / 160 = 实际像素数)计算得出,dp与px的比例是1:3,1080 x 1920的手机就需要在UI设计图中,将px除于3,即可获得dp值
模拟Json数据接口
使用EasyMock的Api,特别简单,只需要输入模拟的Json数据,访问其Api就能获取Json数据注销、切换账号时,顺带销毁主界面
我们在开发的时候,往往会遇到注销、切换账号等等功能,这个时候我们的主界面还没消失,但是我们又要跳转到登陆界面,让主界面自己销毁,这个时候该怎么办。想了很久,忘了还有一个广播可以实现这个需求,当我们注销时,我们可以通过发送广播退出主界面,这样在退出界面的时候就不会回到原先的主界面//在主界面中动态注册广播 IntentFilter filter = new IntentFilter(); filter.addAction("com.hensen.exit.main"); registerReceiver(myReceiver, filter); //创建广播接收者 private BroadcastReceiver myReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { MainActivity.this.finish(); } };
在另外的界面注销、或者切换账号的时候,发送广播,让主界面销毁
sendBroadcast(new Intent("com.hensen.exit.main"));
安卓7.0照相机的适配
在安卓7.0中,官方对照相机的启动做了改变,开发者也要进行适配1、首先需要在Manifest中声明Provider,注意:如果你引入的第三方图片选择库有开启的相机的Provider,那么此时会冲突
<provider android:name="android.support.v4.content.FileProvider" android:authorities="{你的应用的包名}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
2、创建xml目录和创建file_path.xml
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path name="my_images" path="Android/data/com.handsome.teacherproject/files/Pictures" /> </paths>
3、在代码中启动我们的相机,注意:只兼容7.0以上的相机,其他相机采用另一方法
private static int REQUEST_IMAGE_CAPTURE = 0x01; //拍照后照片的路径 private String mCurrentPhotoPath; /** * 开启系统相机 */ private void startPhotoCapture() { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); //判断是否有相机应用 if (takePictureIntent.resolveActivity(getPackageManager()) != null) { File photoFile = null; try { //创建临时图片文件 photoFile = createImageFile(); } catch (IOException ex) { ex.printStackTrace(); } if (photoFile != null) { //FileProvider使用 content:// Uri 代替了 file:// Uri Uri photoURI; if (Build.VERSION.SDK_INT >= 24) { photoURI = FileProvider.getUriForFile(this, "{你的应用的包名}.fileprovider", photoFile); } else { photoURI = Uri.fromFile(photoFile); } takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI); startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE); } } } /** * 创建Image的临时文件 */ private File createImageFile() throws IOException { String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA).format(new Date()); String imageFileName = "JPEG_" + timeStamp + "_"; //.getExternalFilesDir()方法可以获取到 SDCard/Android/data/你的应用的包名/files/ 目录,一般放一些长时间保存的数据 File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES); //创建临时文件,文件前缀不能少于三个字符,后缀如果为空默认未".tmp" File image = File.createTempFile( imageFileName, /* 前缀 */ ".jpg", /* 后缀 */ storageDir /* 文件夹 */ ); mCurrentPhotoPath = image.getAbsolutePath(); return image; } /* * 在返回的结果中拿到图片 */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) { //mCurrentPhotoPath,这个全局变量就是我们拍照后的文件路径,直接想怎么用就怎么用 //这个是高清图,不是缩略图,所以要上传服务器的时候记得压缩后上传 } }
沉浸式状态栏和导航栏
适配4.4-6.0沉浸式状态栏和导航栏@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public class BaseTranslucentActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); //版本[4.4,5.0),设置状态栏和导航栏为透明 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); } } @SuppressLint("NewApi") public void setOrChangeTranslucentColor(Toolbar toolbar, View bottomNavigationBar, int translucentPrimaryColor) { //版本[4.4,5.0),设置状态栏和导航栏为透明 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { if (toolbar != null) { LayoutParams params = toolbar.getLayoutParams(); int statusBarHeight = getStatusBarHeight(this); params.height += statusBarHeight; toolbar.setLayoutParams(params); toolbar.setPadding(toolbar.getPaddingLeft(), toolbar.getPaddingTop() + getStatusBarHeight(this), toolbar.getPaddingRight(), toolbar.getPaddingBottom()); toolbar.setBackgroundColor(translucentPrimaryColor); } if (bottomNavigationBar != null) { if (hasNavigationBarShow(getWindowManager())) { LayoutParams p = bottomNavigationBar.getLayoutParams(); p.height += getNavigationBarHeight(this); bottomNavigationBar.setLayoutParams(p); bottomNavigationBar.setBackgroundColor(translucentPrimaryColor); } } } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { getWindow().setNavigationBarColor(translucentPrimaryColor); getWindow().setStatusBarColor(translucentPrimaryColor); } else { } } /** * 获取虚拟导航栏的高度 * * @param context * @return */ private int getNavigationBarHeight(Context context) { return getSystemComponentDimen(context, "navigation_bar_height"); } /** * 获取状态栏的高度 * * @param context * @return */ private int getStatusBarHeight(Context context) { return getSystemComponentDimen(context, "status_bar_height"); } /** * 反射获取系统的属性 * * @param context * @param dimenName * @return */ private static int getSystemComponentDimen(Context context, String dimenName) { int statusHeight = -1; try { Class<?> clazz = Class.forName("com.android.internal.R$dimen"); Object object = clazz.newInstance(); String heightStr = clazz.getField(dimenName).get(object).toString(); int height = Integer.parseInt(heightStr); //dp--->px statusHeight = context.getResources().getDimensionPixelSize(height); } catch (Exception e) { e.printStackTrace(); } return statusHeight; } /** * 判断底部是否存在NavigationBar * * @return */ private static boolean hasNavigationBarShow(WindowManager windowManager) { Display d = windowManager.getDefaultDisplay(); //获取整个屏幕的高度 DisplayMetrics realDisplayMetrics = new DisplayMetrics(); d.getRealMetrics(realDisplayMetrics); int realHeight = realDisplayMetrics.heightPixels; int realWidth = realDisplayMetrics.widthPixels; //获取内容展示部分的高度 DisplayMetrics displayMetrics = new DisplayMetrics(); d.getMetrics(displayMetrics); int displayHeight = displayMetrics.heightPixels; int displayWidth = displayMetrics.widthPixels; return (realWidth - displayWidth) > 0 || (realHeight - displayHeight) > 0; } }
使用沉浸式状态栏和导航栏
public class MainActivity extends BaseTranslucentActivity{ private Toolbar toolbar; private View nav; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); toolbar = (Toolbar)findViewById(R.id.toolbar); nav = findViewById(R.id.nav); setOrChangeTranslucentColor(toolbar, nav, getResources().getColor(R.color.colorPrimary_pink)); } }
Paint的细节用法
1、设置笔帽mPaint.setStrokeCap(Paint.Cap.BUTT);//没有 mPaint.setStrokeCap(Paint.Cap.ROUND);//圆形 mPaint.setStrokeCap(Paint.Cap.SQUARE);//方形
2、设置滤镜
1、模糊遮罩滤镜(BlurMaskFilter) 2、浮雕遮罩滤镜(EmbossMaskFilter)
3、设置线条汇合处
mPaint.setStrokeJoin(Paint.Join.MITER);//锐角 mPaint.setStrokeJoin(Paint.Join.ROUND);//圆弧 mPaint.setStrokeJoin(Paint.Join.BEVEL);//直线
4、文字相关
//获得字符行间距 mPaint.getFontSpacing(); //获得字符之间的间距 mPaint.getLetterSpacing(); //设置文本删除线 mPaint.setStrikeThruText(true); //设置下划线 mPaint.setUnderlineText(true); //设置文本大小 mPaint.setTextSize(textSize); //设置字体类型 mPaint.setTypeface(Typeface.ITALIC); //加载自定义字体 Typeface.create(familyName, style) //文字倾斜,官方推荐的-0.25f是斜体 mPaint.setTextSkewX(-0.25f); //文本对齐方式 mPaint.setTextAlign(Align.LEFT); mPaint.setTextAlign(Align.CENTER); mPaint.setTextAlign(Align.RIGHT); //计算制定长度的字符串(字符长度、字符个数、真实的长度) int breadText = mPaint.breakText(text, measureForwards, maxWidth, measuredWidth) float[] measuredWidth = new float[1]; int breakText = mPaint.breakText(str, true, 200, measuredWidth); Log.i("TAG", "breakText="+breakText+", str.length()="+str.length()+", measredWidth:"+measuredWidth[0]); //获取文本的矩形区域(宽高) mPaint.getTextBounds(text, index, count, bounds) //获取文本的宽度(比较粗略的结果) float measureText = mPaint.measureText(str); //获取文本的宽度(比较精准的) float[] measuredWidth = new float[10]; int textWidths = mPaint.getTextWidths(str, measuredWidth); Log.i("TAG", "measureText:"+measureText+", textWidths:"+textWidths);
5、基线相关
FontMetrics fontMetrics = mPaint.getFontMetrics(); //绘制文本在View的左上角(基线Y的计算公式) float baselineY = top(已知) - fontMetrics.top; //绘制文本的在View的中间(基线Y的计算公式) float baselineY = centerY(已知)+ (fontMetrics.bottom-fontMetrics.top)/2 - fontMetrics.bottom; //绘制文本的在View的中间(或者是这样的) float baselineY = centerY(已知)+ (mPaint.descent()+mPaint.ascent())/2;
6、渲染相关
LinearGradient线性渲染 RadialGradient环形渲染、可做水波纹效果,充电水波纹扩散效果、调色板 SweepGradient梯度渲染(扫描渲染)、可做微信等雷达扫描效果 ComposeShader组合渲染
7、ColorMatrix(五阶矩阵)
//色彩的平移运算(加法运算) //色彩的缩放运算(乘法运算) //构造方法 ColorMatrix matrix = new ColorMatrix(new float[]{}); ColorMatrix matrix = new ColorMatrix(); matrix.set(src) //设置色彩的缩放函数 matrix.setScale(1, 1, 1.4f, 1); //设置饱和度(1,是原来不变;0灰色;>1增加饱和度) matrix.setSaturation(progress); //色彩旋转函数(axis,代表绕哪一个轴旋转,0红色,1绿色,2蓝色,degrees:旋转的度数) matrix.setRotate(0, progress); //ColorFilter使用的子类 ColorMatrixColorFilter:色彩矩阵的颜色顾虑器。 LightingColorFilter:过滤颜色和增强色彩的方法。(光照颜色过滤器) PorterDuffColorFilter:图形混合滤镜(图形学的一个理论飞跃)
Canvas.drawPath
将文本画在Path上,可以形成圆弧的文本Path path = new Path(); //Path.Direction.CW,沿外环;Path.Direction.CCW,沿内环 path.addCircle(500, 500, 200, Path.Direction.CW); mPaint.setTextSize(50); // 绘制路径 canvas.drawPath(path, mPaint); String text = "大家好,我是Hensen"; canvas.drawTextOnPath(text, path, 0f, 0f, mPaint);
封装UniversalImageLoader
public class ImageManager { private static final int THREAD_COUNT = 2; private static final int PRIORITY = 2; private static final int MEMORY_CACHE_SIZE = 2 * 1024 * 1024; private static final int DISK_CACHE_SIZE = 30 * 1024 * 1024; private static final int CONNECT_TIME_OUT = 5 * 1000; private static final int READ_TIME_OUT = 30 * 1000; private static volatile com.hensen.shixiongsdk.ImageLoader.ImageManager ImageManager; private ImageLoader imageLoader = null; private ImageManager(Context context) { initDefaultConfiguration(context); } public static com.hensen.shixiongsdk.ImageLoader.ImageManager getInstance(Context context) { if (ImageManager == null) { synchronized (com.hensen.shixiongsdk.ImageLoader.ImageManager.class) { if (ImageManager == null) { ImageManager = new ImageManager(context); } } } return ImageManager; } private void initDefaultConfiguration(Context context) { ImageLoaderConfiguration configuration = new ImageLoaderConfiguration .Builder(context) .threadPoolSize(THREAD_COUNT) .threadPriority(Thread.NORM_PRIORITY - PRIORITY) .denyCacheImageMultipleSizesInMemory() .memoryCacheSize(MEMORY_CACHE_SIZE) .diskCacheSize(DISK_CACHE_SIZE) .diskCacheFileNameGenerator(new Md5FileNameGenerator()) .tasksProcessingOrder(QueueProcessingType.LIFO) .defaultDisplayImageOptions(getDefaultOptions()) .imageDownloader(new BaseImageDownloader(context, CONNECT_TIME_OUT, READ_TIME_OUT)) .build(); com.nostra13.universalimageloader.core.ImageLoader.getInstance().init(configuration); imageLoader = com.nostra13.universalimageloader.core.ImageLoader.getInstance(); } private DisplayImageOptions getDefaultOptions() { DisplayImageOptions defaultOptions = new DisplayImageOptions .Builder() .cacheOnDisk(true) .cacheInMemory(true) .considerExifParams(true) .imageScaleType(ImageScaleType.IN_SAMPLE_INT) .bitmapConfig(Bitmap.Config.RGB_565) .decodingOptions(new BitmapFactory.Options()) .resetViewBeforeLoading(true) .build(); return defaultOptions; } public void displayImage(String url, ImageView imageView, DisplayImageOptions options, ImageLoadingListener loadingListener) { imageLoader.displayImage(url, imageView, options, loadingListener); } public void displayImage(String url, ImageView imageView, ImageLoadingListener loadingListener) { displayImage(url, imageView, null, loadingListener); } public void displayImage(String url, ImageView imageView) { displayImage(url, imageView, null); } }
启动页面优化
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //1、ViewStub优化 viewStub = (ViewStub)findViewById(R.id.content_viewstub); //判断当窗体加载完毕的时候,立马再加载真正的布局进来 getWindow().getDecorView().post(new Runnable() { @Override public void run() { mHandler.post(new Runnable() { @Override public void run() { viewStub.inflate(); } } ); } }); //2、判断当窗体加载完毕的时候执行,延迟一段时间执行闪屏页面动画并关闭 getWindow().getDecorView().post(new Runnable() { @Override public void run() { mHandler.postDelayed(new DelayRunnable() ,2000); } }); }
相关文章推荐
- Android开发细节——开发过程中遇到的细节问题与解决方案汇总【转】
- Android项目实战_新浪微博客户端开发过程中遇到的问题及解决办法01
- 最近这段时间开发过程中遇到的一些细节问题汇总
- Android开发过程遇到的安装好的APP打开程序崩溃,或者安装后应用列表里没有的问题及解决方案
- android开发过程中遇到的一些问题汇总
- PHP实战——开发遇到过的错误问题与解决方案汇总
- Android开发错误——Android Studio中遇到过的错误问题与解决方案汇总
- android开发过程遇到的问题和解决方案(不断更新)
- android 开发过程中遇到的问题及解决方案
- Android游戏开发中使用Libgdx引擎遇到的问题及解决办法汇总
- 工作流引擎组件[行政审批流程组件] 开发实施过程中遇到的问题汇总,希望能对大家有参考价值
- android开发过程中遇到的小问题
- 今天在从装系统后在配置android开发环境的过程中遇到了一点小问题
- Android开发遇到的细节问题(一)
- Html5 Builder 开发Android程序过程中遇到问题解决
- 第三方登录开发过程中遇到问题及解决方案
- android开发中经常遇到的问题汇总
- 工作流引擎组件[行政审批流程组件] 开发实施过程中遇到的问题汇总,希望能对大家有参考价值
- 开发过程中遇到的问题总结--qt for android
- android开发过程中遇到的小问题