Android开发细节——开发实战过程中遇到的细节问题与解决方案汇总

来源:互联网 发布:js 通过class获取元素 编辑:程序博客网 时间:2024/05/30 23:55

获取系统时间的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会事先加载好当前页的前后两页,也就是到了第三页的时候,第一页已经被销毁了,回到第二页的时候会重新创建

解决方法

//默认是1mViewPager.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中添加下面的处理即可

@Overridepublic 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;}/* * 在返回的结果中拿到图片 */@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {    super.onActivityResult(requestCode, resultCode, data);    if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {        //mCurrentPhotoPath,这个全局变量就是我们拍照后的文件路径,直接想怎么用就怎么用        //这个是高清图,不是缩略图,所以要上传服务器的时候记得压缩后上传    }}
阅读全文
1 0
原创粉丝点击