android开发中获取手机上可用SD卡方法分享

 更新时间:2016年9月20日 19:57  点击:2294
本文我们来分享android开发中怎么样获取手机上所有的可用的外置SD卡的方法,这个功能非常有用,因为一般人的都会外置SD卡。

现在的android手机型号复杂多样,造成了开发过程中使用官方的获取sd卡的方法在部分的手机上并不适用,所以需要进行开发的自己封装,以下就是代码,希望分享出来,大家共同学习

/**
* 获取手机sd卡的工具类
* @author wy
*/
public class SDCardUtils {
/*
* avoid initializations of tool classes
*/
private SDCardUtils() {
// TODO Auto-generated constructor stub
}

/**
* @Title: getExtSDCardPaths
* @Description: to obtain storage paths, the first path is theoretically
* the returned value of
* Environment.getExternalStorageDirectory(), namely the
* primary external storage. It can be the storage of internal
* device, or that of external sdcard. If paths.size() >1,
* basically, the current device contains two type of storage:
* one is the storage of the device itself, one is that of
* external sdcard. Additionally, the paths is directory.
* @return List<String>
* @throws IOException
* 获取手机上所有可用的sd卡路径
* @return
*/
public static ArrayList<String> getExtSDCardPaths() {
ArrayList<String> paths = new ArrayList<String>();
String extFileStatus = Environment.getExternalStorageState();
File extFile = Environment.getExternalStorageDirectory();
if (extFileStatus.equals(Environment.MEDIA_MOUNTED) && extFile.exists()
&& extFile.isDirectory()) {
paths.add(extFile.getAbsolutePath());
}
try {
// obtain executed result of command line code of 'mount', to judge
// whether tfCard exists by the result
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec("mount");
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = null;
int mountPathIndex = 1;
while ((line = br.readLine()) != null) {
// format of sdcard file system: vfat/fuse
if ((!line.contains("fat") && !line.contains("fuse") && !line
.contains("storage"))
|| line.contains("secure")
|| line.contains("asec")
|| line.contains("firmware")
|| line.contains("shell")
|| line.contains("obb")
|| line.contains("legacy") || line.contains("data")) {
continue;
}
String[] parts = line.split(" ");
int length = parts.length;
if (mountPathIndex >= length) {
continue;
}
String mountPath = parts[mountPathIndex];
if (!mountPath.contains("/") || mountPath.contains("data")
|| mountPath.contains("Data")) {
continue;
}
File mountRoot = new File(mountPath);
if (!mountRoot.exists() || !mountRoot.isDirectory()) {
continue;
}
boolean equalsToPrimarySD = mountPath.equals(extFile
.getAbsolutePath());
if (equalsToPrimarySD) {
continue;
}
paths.add(mountPath);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return paths;
}

/**
* SD卡剩余空间大小
* @param path 取得SD卡文件路径
* @return 单位MB
*/
public static long getSDFreeSize(String path) {
StatFs sf = new StatFs(path);
// 获取单个数据块的大小(Byte)
long blockSize = sf.getBlockSize();
// 空闲的数据块的数量
long freeBlocks = sf.getAvailableBlocks();
// 返回SD卡空闲大小
return (freeBlocks * blockSize) / 1024 / 1024; //
}

/**
* SD卡总容量
* @param path 取得SD卡文件路径
* @return 单位MB
*/
public static long getSDAllSize(String path) {
StatFs sf = new StatFs(path);
// 获取单个数据块的大小(Byte)
long blockSize = sf.getBlockSize();
// 获取所有数据块数
long allBlocks = sf.getBlockCount();
// 返回SD卡大小
// return allBlocks * blockSize; //单位Byte
// return (allBlocks * blockSize)/1024; //单位KB
return (allBlocks * blockSize) / 1024 / 1024;
}

/**
* 判断当前内存卡是否可用
* @param mContext
* @return
*/
public static final boolean isExist(String sdPath) {
ArrayList<String> list = getExtSDCardPaths();
for (int i = 0; i < list.size(); i++) {
if(list.contains(sdPath)) {
return true;
}
}
return false;
}
}


Android手机外置SD卡(TF卡)的获取方法

    Android手机上的外置SD卡,起初的时候,即在Android出世的前几年,那时手机的存储是十分有限的,不像现在到处可见16G、32G和64G的存储,因而那时候的手机有的厂商允许插入外置的SD卡,此时这张卡仍处于手机的扩展部分。后来,随着手机的发展以及存储能力的增加,这张外置SD卡,逐渐成为了手机的一部分,不再允许可挺拔了,当然现在依然有的手机允许对存储进行拓展,比如三星等。

    那张拓展的存储卡,现在叫做TF卡,且不是所有的手机都支持它,但是有时候有些奇葩需求偏要优先存储在TF卡里面,这叫不得不要求开发人员去检查这张卡是否存在、是否可用。又因为这是手机厂商可拓展、可自定义的部分,所有不同厂商生产的手机,以及同一厂商生产的不同型号的手机,TF卡的位置都相差很大,并没有一个统一的名称或位置。因而这是比较困难的一部分,但是还好Android是开源的,我们可以通过运行时来判断手机是否有TF卡,以及TF卡是否可用。

    下面这个方法可以获取手机的可以存储,包括SD卡、TF卡等,对多存储卡进行了匹配,详细的代码如下:

     1 public class SDCardScanner {
     2     /*
     3      * avoid initializations of tool classes
     4      */
     5     private SDCardScanner() {
     6     }
     7
     8     /**
     9      * @Title: getExtSDCardPaths
    10      * @Description: to obtain storage paths, the first path is theoretically
    11      *               the returned value of
    12      *               Environment.getExternalStorageDirectory(), namely the
    13      *               primary external storage. It can be the storage of internal
    14      *               device, or that of external sdcard. If paths.size() >1,
    15      *               basically, the current device contains two type of storage:
    16      *               one is the storage of the device itself, one is that of
    17      *               external sdcard. Additionally, the paths is directory.
    18      * @return List<String>
    19      * @throws IOException
    20      */
    21     public static List<String> getExtSDCardPaths() {
    22         List<String> paths = new ArrayList<String>();
    23         String extFileStatus = Environment.getExternalStorageState();
    24         File extFile = Environment.getExternalStorageDirectory();
    25         if (extFileStatus.endsWith(Environment.MEDIA_UNMOUNTED)
    26                 && extFile.exists() && extFile.isDirectory()
    27                 && extFile.canWrite()) {
    28             paths.add(extFile.getAbsolutePath());
    29         }
    30         try {
    31             // obtain executed result of command line code of 'mount', to judge
    32             // whether tfCard exists by the result
    33             Runtime runtime = Runtime.getRuntime();
    34             Process process = runtime.exec('mount');
    35             InputStream is = process.getInputStream();
    36             InputStreamReader isr = new InputStreamReader(is);
    37             BufferedReader br = new BufferedReader(isr);
    38             String line = null;
    39             int mountPathIndex = 1;
    40             while ((line = br.readLine()) != null) {
    41                 // format of sdcard file system: vfat/fuse
    42                 if ((!line.contains('fat') && !line.contains('fuse') && !line
    43                         .contains('storage'))
    44                         || line.contains('secure')
    45                         || line.contains('asec')
    46                         || line.contains('firmware')
    47                         || line.contains('shell')
    48                         || line.contains('obb')
    49                         || line.contains('legacy') || line.contains('data')) {
    50                     continue;
    51                 }
    52                 String[] parts = line.split(' ');
    53                 int length = parts.length;
    54                 if (mountPathIndex >= length) {
    55                     continue;
    56                 }
    57                 String mountPath = parts[mountPathIndex];
    58                 if (!mountPath.contains('/') || mountPath.contains('data')
    59                         || mountPath.contains('Data')) {
    60                     continue;
    61                 }
    62                 File mountRoot = new File(mountPath);
    63                 if (!mountRoot.exists() || !mountRoot.isDirectory()
    64                         || !mountRoot.canWrite()) {
    65                     continue;
    66                 }
    67                 boolean equalsToPrimarySD = mountPath.equals(extFile
    68                         .getAbsolutePath());
    69                 if (equalsToPrimarySD) {
    70                     continue;
    71                 }
    72                 paths.add(mountPath);
    73             }
    74         } catch (IOException e) {
    75             // TODO Auto-generated catch block
    76             e.printStackTrace();
    77         }
    78         return paths;
    79     }
    80 }


    首先,我把它写成了一个工具类,因而声明了一个私有的构造器,目的就是要防止该类被实例化。

    然后,首先获取了Android标准一部分的外置SD卡,如果它可用的话。

    然后利用运行时,通过命令行函数'mount'来获取所有的存储位置,并对返回的结果进行SD卡或者TF卡的查找。

    最后返回了所有可用于存储的不同的卡的位置,用一个List来保存。由于不是所有的手机都支持TF卡,因而这个List包含的路径未必很多,只有一个SD卡的手机只会返回一个路径,多个可用存储位置的会返回多个路径。

    但有一点,是必须的,paths.get(0)肯定是外置SD卡的位置,因为它是primary external storage.

本文我来分享一下我的自定义dialog的样式的实现方法,其实也非常简单,请看后面我给出的实例。

在网上找了很多关于dialog的自定义样式的问题,还有很多人写得比较复杂,需要改动style什么的,或者是自定义dialog搞得很复杂,我最后还是找到了方法来实现。

下面是我的dialog布局xml文件:

[mw_shl_code=java,true]<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/transparent"
    android:layout_margin="50dp">
   
    <RelativeLayout
        android:id="@+id/rl_dialog_content"
        android:layout_width="fill_parent"
        android:layout_height="200dp"
        android:background="@drawable/alertdialog_bg">
            <TextView
                android:id="@+id/dialog_text"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="现在就打电话给客服:arjinmc"           
                android:layout_marginTop="50dp"     
                android:layout_marginLeft="30dp"
                android:layout_marginRight="30dp"
                android:maxLines="5"
                android:gravity="center"
                />        
               
            <LinearLayout
                android:id="@+id/ll_buttons"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal"
                android:layout_alignParentBottom="true"
                android:layout_margin="30dp">
               
               
                <Button
                 android:id="@+id/dialog_cancel"
                 android:text="@string/alert_cancel"
                 android:background="@drawable/btn_long_white"
                 style="@style/dialog_button"
                 android:layout_weight="1"
                 android:textColor="@color/tabs_font"
                 />
                <Button
                 android:id="@+id/dialog_ok"
                 android:text="@string/alert_ok"
                 android:background="@drawable/btn_long_red"
                 style="@style/dialog_button"
                 android:layout_weight="1"
                 android:layout_marginLeft="5dp"/>
               
            </LinearLayout>
    </RelativeLayout>
    <ImageButton
        android:id="@+id/dialog_close"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/alertdialog_close"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="20dp"
       />   
   

</RelativeLayout>
[/mw_shl_code]


在代码中需要这样写就可以了:
[mw_shl_code=java,true]//布局文件转换为view对象
  LayoutInflater inflaterDl = LayoutInflater.from(this);
  RelativeLayout layout = (RelativeLayout)inflaterDl.inflate(R.layout.layout_dialog, null );
 
  //对话框
  final Dialog dialog = new AlertDialog.Builder(SettingActivity.this).create();
  dialog.show();
  dialog.getWindow().setContentView(layout);
 
 
  //取消按钮
  Button btnCancel = (Button) layout.findViewById(R.id.dialog_cancel);
  btnCancel.setOnClickListener(new OnClickListener() {
        
        @Override
        public void onClick(View v) {
                Toast.makeText(getApplicationContext(), "cancel", Toast.LENGTH_SHORT).show();                                
        }
  });
 
 
  //确定按钮
  Button btnOK = (Button) layout.findViewById(R.id.dialog_ok);
  btnOK.setOnClickListener(new OnClickListener() {
        
        @Override
        public void onClick(View v) {
                Toast.makeText(getApplicationContext(), "ok", Toast.LENGTH_SHORT).show();                                
        }
  });
 
 
  //关闭按钮
  ImageButton btnClose = (ImageButton) layout.findViewById(R.id.dialog_close);
  btnClose.setOnClickListener(new OnClickListener() {
        
        @Override
        public void onClick(View v) {
                dialog.dismiss();                                
        }
  });[/mw_shl_code]


非常easy!自己动手吧。



自定义dialog的样式并比较日期

<style name="myDialogTheme" parent="android:style/Theme.Dialog">
<item name="android:windowNoTitle">true</item>
</style>


1、布局main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />
    <Button
        android:id="@+id/btn"
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        android:text="按下按钮试试"/>

</LinearLayout>

2、ly_dialogcontent

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="220dp"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:gravity="center"
    android:layout_gravity="center"
    android:background="#88E0EEEE">
    <LinearLayout
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        android:orientation="vertical">
        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:textColor="@android:color/white"
            android:textSize="20sp"
            android:text="查询"
            android:gravity="center"
            android:layout_gravity="center"
            android:padding="10dp"/>
        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:gravity="center">
            <TextView
                
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                android:text="开始时间:"/>
            <EditText
                android:id="@+id/beginTime"
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                android:singleLine="true"/>
          </LinearLayout>
          <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:gravity="center">
            <TextView
                
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                android:text="开始时间:"/>
            <EditText
                android:id="@+id/endTime"
                android:layout_height="wrap_content"
                android:layout_width="wrap_content"
                android:singleLine="true"/>
          </LinearLayout>
          <LinearLayout
              android:layout_width="fill_parent"
              android:layout_height="wrap_content"
              android:gravity="center">
              <Button
                  android:id="@+id/search"
                  android:layout_height="wrap_content"
                  android:layout_width="wrap_content"
                  android:layout_weight="1.0"
                  android:text="查询"/>
              <Button
                  android:id="@+id/cancel"
                  android:layout_height="wrap_content"
                  android:layout_width="wrap_content"
                  android:layout_weight="1.0"
                  android:text="取消"/>
              
          </LinearLayout>
        
    </LinearLayout>

    

</RelativeLayout>

3、Mainactivity

package com.ct.dialog;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import android.app.Activity;
import android.app.DatePickerDialog;
import android.app.DatePickerDialog.OnDateSetListener;
import android.app.Dialog;
import android.app.TimePickerDialog;
import android.app.TimePickerDialog.OnTimeSetListener;
import android.os.Bundle;
import android.text.InputType;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.TimePicker;
import android.widget.Toast;

public class MainActivity extends Activity {
    /** Called when the activity is first created. */
    private Button btnBn;
    private Dialog dlg;
    private LayoutInflater mInflater;
    private Calendar calendar;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        btnBn = (Button)findViewById(R.id.btn);
        init();
        btnBn.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                dlg.show();
            }
        });
    }
    
    private void init(){
        calendar = Calendar.getInstance();
        dlg = new Dialog(MainActivity.this, R.style.myDialogTheme);
        mInflater = LayoutInflater.from(MainActivity.this);
        dlg.setCancelable(true);
        dlg.setCanceledOnTouchOutside(true);
        
        View view = mInflater.inflate(R.layout.ly_dialogcontent, null);
        final EditText begin = (EditText)view.findViewById(R.id.beginTime);
        final EditText end = (EditText)view.findViewById(R.id.endTime);
        Button sbtn = (Button)view.findViewById(R.id.search);
        Button clebt = (Button)view.findViewById(R.id.cancel);
        begin.setInputType(InputType.TYPE_NULL);
        end.setInputType(InputType.TYPE_NULL);
        dlg.setContentView(view);
        //取消
        clebt.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                dlg.dismiss();
            }
        });
        
        //查询
        sbtn.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                String beginTime =  begin.getText().toString();
                String endTime = end.getText().toString();
                if (beginTime.equals("")|| endTime.equals("")) {

                    Toast.makeText(MainActivity.this, "都不能为空", Toast.LENGTH_LONG).show();

                }else if(!isLarge(beginTime, endTime)){
                    Toast.makeText(MainActivity.this, "结束时间不能比开始时间小", Toast.LENGTH_LONG).show();
                } else {
                    dlg.dismiss();}
            }
        });
        
        //开始时间
        begin.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                final String second = "00";
                DatePickerDialog dpg = new DatePickerDialog(MainActivity.this,
                        new OnDateSetListener() {
                            
                            @Override
                            public void onDateSet(DatePicker view, int year, int monthOfYear,
                                    int dayOfMonth) {
                                // TODO Auto-generated method stub
                                begin.setText(year+"-"+
                                format(++monthOfYear)+"-"+
                                format(dayOfMonth));
                                
                                TimePickerDialog tpg = new TimePickerDialog(MainActivity.this,
                                        new OnTimeSetListener(){

                                            @Override
                                            public void onTimeSet(
                                                    TimePicker arg0, int hourOfDay,
                                                    int minute) {
                                                // TODO Auto-generated method stub
                                                begin.setText(begin.getText().toString()
                                                        + " "
                                                        + format(hourOfDay)
                                                        + ":"
                                                        + format(minute)
                                                        + ":" + second);
                                            }},calendar.get(Calendar.HOUR_OF_DAY),
                                        calendar.get(Calendar.MINUTE), true);
                                tpg.show();
                            }
                        }, calendar.get(Calendar.YEAR),
                        calendar.get(Calendar.MONTH),
                        calendar.get(Calendar.DAY_OF_MONTH));
                
                dpg.show();
            }
        });
        //结束时间
        end.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                final String second = "00";
                DatePickerDialog dpg = new DatePickerDialog(MainActivity.this,
                        new OnDateSetListener() {
                            
                            @Override
                            public void onDateSet(DatePicker view, int year, int monthOfYear,
                                    int dayOfMonth) {
                                // TODO Auto-generated method stub
                                end.setText(year+"-"+
                                format(++monthOfYear)+"-"+
                                format(dayOfMonth));
                                
                                TimePickerDialog tpg = new TimePickerDialog(MainActivity.this,
                                        new OnTimeSetListener(){

                                            @Override
                                            public void onTimeSet(
                                                    TimePicker arg0, int hourOfDay,
                                                    int minute) {
                                                // TODO Auto-generated method stub
                                                end.setText(begin.getText().toString()
                                                        + " "
                                                        + format(hourOfDay)
                                                        + ":"
                                                        + format(minute)
                                                        + ":" + second);
                                            }},calendar.get(Calendar.HOUR_OF_DAY),
                                        calendar.get(Calendar.MINUTE), true);
                                tpg.show();
                            }
                        }, calendar.get(Calendar.YEAR),
                        calendar.get(Calendar.MONTH),
                        calendar.get(Calendar.DAY_OF_MONTH));
                
                dpg.show();
            }
        });
        
        
    }
    
    /**
     * 比较两个时间的大小
     * @throws ParseException
     *
     * */
    public static boolean isLarge(String beginTime,String endTime) {
        boolean flag = false;
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date begin = sdf.parse(beginTime);
            Date edn = sdf.parse(endTime);
            if(edn.getTime() - begin.getTime() >0){
                flag = true;
            }else {
                flag = false;
            }
            
        } catch (ParseException e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        return flag;
        
    }
    
    
    /**
     * 在一位数字前加0变成两位数字
     *
     * @param value
     * @return
     */
    private String format(int value) {
        String s = String.valueOf(value);
        if (s.length() == 1) {
            s = "0" + s;
        }
        return s;
    }
}

 (在F:\java\z自定义dialog+点击弹出时间调整那个\MyDialogTheme)

本文我们来分享三篇Android开发人员的面试经历,这些亲身的经历可以给新人或者准备面试的Android开发者提供参考。

一个Android菜鸟“面霸”的面试经历分享


今天是我入职一个创业公司的第二天。由于公司今天才把自己用的电脑买回来,只好今天在做着每一个技术人员入职第一天常做的事:安装相关开发软件,配置开发环境。

在安装软件的过程中,由于下载最新版的android studio比较缓慢,只好在下载过程中随便翻翻。然后,看到一个开发群里,一位朋友还在聊面试的事,他在那里说,今天又没有面试通知。听了之后,感慨万千,这样的感受我也有过很多次。

至于为什么敢称自己“面霸”,一来有噱头的成分,二来,确实自己面试了很多次。这不是自己有什么自豪的地方,确实是因为自己的工作经历有点特殊。差不多四年的时间,换了四份工作,每一次换工作都会不得不开始找工作。有些是因为个人发展原因,有些是因为一些公司项目组解散,公司管理方面的原因。

去年我最多的一次面试是一天赶四家面试,我以为从那以后,再也不会有这样的情况了。但是,上周找工作,一周面试了15家,除了第一天面试了一家,后面四天的面试是3个,4个,3个,4个。一天四场面试,中午都是没时间吃饭的,饿着肚子跑了一天。最后拿到了三个比较满意的offer。在上海的两年,因为换工作,上海的地铁线,除了一个最近新开的16号没走过,其余的都走过。然后也遇到过各种奇葩、坑爹的面试。面试我的人组长,主管,技术总监,CTO,老板,还有不懂技术的各种人物,有上市公司、初创公司,外包、外派公司,有互联网公司,IT公司,也有什么销售、广告、传媒、金融公司。说了这么多,不是说自己技术好,我只是因为自身的经历,看到了很多现象,才想说出来,能帮到一些找工作的朋友,就善莫大焉了。

这里简单说一下自己的情况,大专学历,之前学的机电,然后参加一个Java培训,培训之后,自己发现javaee开发需要用到的知识太多了,然后,自己刚好那个时候接触到android,于是,就想做Android开发。

第一份安卓工作,自己进去差不多就是实习生的身份,因为公司给学习机会,可以一边学习,一边跟着做项目,所以,到现在还是很感谢当初第一份工作。

当然,说到刚开始找工作,因为没有android开发经验,去网上搜了很多职位,投了简历都没反应,搜了很久才找到那家公司,要求很低,实习生都可以,我才去面试,还有上机测试,但是,因为测试的都是基础知识,然后,那家公司是在一个县级市的软件园区,估计,也因为去面试的人,加上当初自己表现的比较愿意学习,老板才给了这次机会。不管怎么说,我从心底感谢第一份工作。

这里的体会是:当你工作经验很少时,你找一个工作要付出很多努力。有很多人说,没人通知面试,面试机会少。刚开始确实是这样,但是,你自己要去找方法。比如:多在几个网站上投,不要怕他们要求高,你条件达不到,万一有面试机会呢?另外,有时间多刷刷简历,一般上午10点左右,下午2-3点左右刷一下简历,这两个点是hr看简历相对集中的时候。另外,你经验少,你要表现出两点,一、基础知识要扎实。二、愿意去学习,至少你要表现出一副以后愿意在这个方向好好学习的样子,要不然,别人干嘛要你?谁愿意去培养你。

说到基础知识扎实,有人说我不扎实,这就是你的问题了。另外,急着找工作,怎么办?

你就搜搜大多数的面试题经常问那些问题,提起多看几遍,不至于问到的时候,根本不知道说什么,至少你能说出个大概也比什么都说不出来好。当然,这不是教你诈,是你找到工作后,要赶紧把自己薄弱的地方补上来。

记得刚开始学Java时,经常会被问到一些问题,比如,Java的三大特性,抽象类与接口的区别,重载与重写,单例模式,递归算法,多线程,这些经常碰到的问题。放到安卓上,可能刚开始经常会问道一些基本的知识:比如,4大组件,5大布局,listview,Intent的使用。面试时这些最常用的知识你要知道,要说的上来吧。

刚开始工作的一两年,面试的时候,碰到出面试的公司大概90%以上,当然,这个各种公司参差不齐的,有的是自己出的有深度的题,有的是从百度上搜的一份打印的,我见过几家直接能搜到原题的面试。先不说出面试的公司怎样,说说我碰到的各种面试的,有全英的,有全部是逻辑题的,就是给你三个图形,让你推测第四个图形的,有面试安卓开发,出了一对c++和sqlserver的题的(公司是做游戏的)。有些面试题做起来真的是浪费时间,如果你感觉这家公司很不满意,就礼貌的说出来,不做了走人吧。如果是正儿八经的面试题,你做不出来时,最好能写写大致的思路,有时候也能算做对的。

说到面试题,有的公司的面试题,真的是多年不变的,我碰到过两家公司,隔一年去面试,还是那几道题,当然有家公司第一次面试过了,因为面临其他的一个机会没有去,第二次面试还是那些题,依然也过了。但是,同样的题,不同的经验,你要答出不同的答案,至少要比之前你回答的更有深度。

其实,你工作大概两三年之后,面试做面试题的公司已经很少了,但是也有15%左右,还是会有面试的。只不过,更多的会问你项目情况,一些实际的开发方面碰到的问题。

比如,现在经常会问到一些,listview的优化技巧,自定义控件的步骤,异步加载方面,jni,ndk,然后一些开源框架,开源控件的使用,并且经常被问到,这些开源控件的源码你看过没有。因为相信大家都明白,很多开源控件都用过,但是,真正去看他们源码,去改他们源码的,恐怕不多吧。还有一些框架设计,安卓源码方面的问题。当然,如果你带过团队,也会问一些团队分工,管理方面的问题。

我记得一个同学的qq签名是,能力越大,责任越大。我想到的是,人是要不断成长的,当你不断的成长的时候,你的责任是不自觉的加给你的。比如,你三年经验做的事和一年经验做的事,肯定有不少差别的,当然你们待遇也有不少差别。

面试,真的是个体力活加脑力活。你一次次面试,要学会总结,其实,从面试中,我们能学到很多东西。比如,你去一个公司面试,要记得看公司环境,地理位置,公司环境,办公室设备,都能看出一个公司资金背景情况。因为,上海这个城市,真的有很多坑爹的创业公司,说白了就是几个人想到一个app创意,就组建一个团队开始做,做不长,资金或者融资跟不上就死掉了。我面试过一家在唐镇的“公司”,我拿着地图找到那里之后,“公司”对面还有人在炒菜,闻起来应该是洋葱炒鸡蛋。公司是直接在小区里租的那种三室一厅,然后,把了七八台电脑,还不交税,直接发现金,我随便聊聊就赶紧走了。其实,这样的公司上海真的很多,见过好多小区房里面上面挂着“禁止在住宿房办公”牌子,但是里面是一个个小公司在关着门上班,第一道大门还在里面反锁着。还有一些所谓的高新园区,集电港什么的,里面一两个小房间,密密麻麻的摆了十几张电脑桌。我不知道,里面上班的人做何感想。我只能说,我们程序员真命苦,要在这样压抑的环境中上班。

所以,对于面试,我个人的推荐是,如果刚毕业,外包、外派的这样的公司,可以迅速的通过大量的项目开发提升自己的技术水平,但是,不要待的太长,除非你有机会进到管理岗位。一般不要超过两年。

两年以上经验的话,有机会进一些大公司,比如,一些大型IT公司或者互联网公司,这样最好。如果进不了大公司的话,进创业公司也是一个不错的选择,关键是,你进一个什么样的创业公司。最近几年移动互联网的发展,造成很多创业公司或者很多创业团队,想到一个点子,或者他们所谓的创意,就迅速的招三五个开发人员,开始开发,开发几个月之后,项目over了,团队解散了。对于创业公司,你一定要擦亮眼睛,进去之前,要问清楚公司做什么产品,公司的团队构成,从一些背景方面,了解公司的资金情况,在要做的产品方面的资源优势,如果产品前景不好,公司有没有资金或者资源优势,这样的创业公司你要慎重考虑了。不要轻易的听信一些面试官,或者老板的忽悠,拿什么前景忽悠你,只能说这么多了。

说一些面试的坑爹经历:

1.一家公司,在一个火车站附近的商业大厦里,进去之后,一看就三个中年人,摆了几台电脑,屋里光线很暗,上来让填一个表,刚准备写,犹豫了一下,我说,不好意思,我不喜欢这样的工作环境(太压抑了),把表还给他们,走了。

2. 有一家金融公司,准备组建团队做自己的金融app,(发现15年准备做自己金融APP的公司特别多)。进去面试,三个西装革履的人,提各种问题,从开发细节,到产品流程,到产品与开发的沟通,问得非常详细,坑爹的地方在于,三个人都拿了一个笔记本,我回到的一些问题,他们觉得重要的都记了下来。比如,我说,我们开发一般需要一个功能文档,和ui流程图、UI效果图。问了一个多小时,他们也记了一个多小时。然后,等通知,没有下文。分明就是想免费了解一些项目开发方面的东西。

3.刚来上海时,有一家公司面试要上机,一定要我建个工程,写个东西,哪怕只是一个button点击事件。

4.面试一家公司的android开发,给了一份c++测试题和一个SQLServer方面的题。我以为拿错了,又问了一下前台,她说,我们招android的就是做这个题。

5.碰到过一家公司的填表问,“你为什么要应聘工作?”、“你家庭生活怎么样?”类似的问题。

6.一个公司在居民楼里,对面的房间还在炒菜。就是上面说的,洋葱炒鸡蛋。

7.去一个比较大的公司,看到填表上有,一面,二面,三面,四面。礼仪评分,穿

着评分,口才评分....问题是,一面是10分钟结束,二面要等下次。为了这一个面试你要跑四次。遂一面之后,放弃。

8. 还是一个金融公司的面试,面试馆拿了一个厚厚的打印资料,回到问题的时候,看他翻页,无意间瞥到了首页上写着几个打字“安卓面试题集”,问的问题,乱七八糟。五大布局叫什么名字,activity的生命周期,java的三大特性,aldi叫什么,等等,很多是一些基础的,网上搜来的面试题。

9.有一家公司,打电话过去面试,我刚填完面试表,刚做完大致介绍,说,我们给不了你这么高的薪资,然后,薪资擦了一大截。然后,结束了,

10.四张全英的面试题,中间两页是逻辑图,全是什么图形推断,数字推断的题。

11.过去先做一个小时的试题,做完了说,先回去吧,我们会电话通知的...

印象比较深刻的就说这几个吧,还有其他的一些,什么过去面试等半天,不懂技术的人,揪着你一个问题一直不放的都有。

说一些面试准备方面的问题:

1.提前查好路线,乘车方式。(我一般是写在纸上,包括联系方式,然后用百度地图,不过,有定位不准的情况)

2.提前了解一下面试公司的一些职位信息,大概需要的一些技术点,可能问到的一些问题。

3.无论是多大的公司,自己多心仪的公司,或者自己不喜欢的公司,都要尽量淡然对待,保持自信,哪怕装你也要装的有自信的样子。

说一些面试收获上的东西,之前看到过一句话说,你要割一段时间就要刷新下简历,不要在一个环境里待的太久。先不说这句话的对错。不同的人有不同的理解。我是因为有些自身的经历,换工作城市,进的某些公司项目组解散,等等原因,才造成换工作比较频繁。不得不一直走在面试的路上。但是,在面试过程中,自己也确实学到了不少东西。跟一些CTO,技术经理面试,他们有的提的问题,包括给的一些建议都很中肯,有一个技术经理也跟我分享了他刚毕业几年的工作经历。还有之前去国内一个微博巨头公司下的一个游戏公司面试,那位美丽的hr负责人姐姐,也给了很多建议,让我弄清楚了自己到底想找什么样的工作。因为刚开始几年,通过跳槽,确实薪资提升比较快,但是,5年以后呢,如果技术没有深入的一个方面,恐怕就不能持续发展了。

说到上个星期之前,其实找到了一家,但是只上了一天班。第二天,我做公交做到地铁站,徘徊了很久,没有进去,就没去那家公司了,虽然那家公司给的薪资不低,离自己住的地方也进,但是,公司一些其他资源不足,能很清楚地看到那一个项目做不长,自己不想做几个月之后,再换工作。然后,才有了上周比较疯狂但是有选择的面试。

说了这么多,只是想分享一些自己的切身经历和感受,希望能给一些最近正着找工作的朋友一些帮助。每个人都有自己的独特性,存在的都是合理的,技术人千万不要唯技术论,觉得自己技术很牛,就可以目无别人,别人比你薪水高,比你受欢迎,一定有他独特的地方。每个人的经历不同,你要找到适合你的方法,你才能更好的在自己的路上走下去。

回首向来萧瑟处,也无风雨也无晴。

梦里不知身是客,一晌贪欢。


分享一下android面试经历,应届生,非211


年前不是找工作的好时间。

码头大了,船自然会来。

找工作前前后后找了差不多3个周了,我呢是应届生,非211,确切地说是大四学生,很多人应该和我差不多,如果你真的有能力,哪个公司会不要你?

写程序很多时候不是经验问题。很多时候是一个思想,回来了,开始写。

还有,我以应届生进去的,因为没有毕业证,所以先实习。

论坛还是有帮助的

11.11好像

第一家公司:新媒传信,不知道?总得知道飞信吧。好了,第一家面试,非常紧张,笔试答的本来就不好,我只说我记得的,题目挺多的,NND,根本打不完,有个算法题,优化问题,总之优化问题是用空间换时间,不过面试官说他也不会,感到平衡许多。。。。总之,各种原因 悲剧了,有人说加班多,这个到无所谓。

11.12应该

橡果国际,名字很好听的,没有笔试直接,当时坐地铁,没有直达的,还得坐bus,直接搞得我头晕,他们在4层,进去前台不在,打电话问,带回前台来了问:是不是看没人没敢进? 我汗,那是礼貌问题,直接无语了,没什么好说的,很失望,真是只有一个好名字。

后面各种小公司

挑部分说,有些我也记不住。

因为我是应届生,一般都背着电脑,有个项目是平板的。所以,,,都说丽水桥附近的骗子公司多,不知道是不是这样。有一个panyun,linux打字不爽啊,用拼音了,进去说流程40分钟,上级,一道算法,一道app,我对硬件还是挑剔的。我想开发的内存要4G才行,打开eclipse有点卡,接下来问题来了,我愁了愁cpu ATOM顿时没了做题的欲望了。

我不是说小公司怎么样,不是那个意思。

面试了差不多10家吧。

有 eico desigin的,做设计的都知道这家公司。

因为紧张,应届生没有经验啊。很多事情毕竟不知道。跟那些工作很多年,跳槽很多次的我当然没法比。

在西城区有一家公司,名字忘了。笔试不错,面试除了有点紧张。我从面试官哪儿获得了对于一个应届生的要求:

1 基础是否扎实

2 沟通意识,团队合作能力

3 吃苦能力和求知欲

这些对于我来说不是什么问题了,可是你还要找到你的缺点是什么,虽然不够致命。

前几天去AppWill面试官也是山东的,挺和气的,然后呢说了说我,普通话问题,虽然做技术的,交流问题我知道很重要。

平时说话有点快,普通话基本不说,在学校说普通话遭围观啊。

第二天AppWill说提供一个职位,当时我说考虑考虑,这是上周三,我是上周以接到京东的面试说:下周二面试。11.29号了

大家都说待遇问题,我也说说自己的观点,记得有人说如果一个人价值8k,hr砍价到7.5k,虽然砍掉了500,也凉了人家的心,如果有更好的机会人家肯定会走。后面还有,不说了。基本就是这个事。

不是每个企业都喜欢培养人的,应该叫做人才储备,对吧。

我知道很多人面试都碰壁,跟研究生我们肯定有差距,不是211也没关系,谁让我们高中贪玩呢,是吧。

业内都知道做加简历,北京很严重,朋友跟我说过,他们面试也是睁一只眼闭一只眼,我也很头疼实习就实习吧,反正没有毕业证。这样更是悲剧,原因很简单,其实吧,大家都说我精通什么什么,你说我熟悉什么什么,不说了。

其中获得了2个内部推荐,一个新浪的,一个京东了,因为新浪那个,当时没有经验,所以跟新浪无缘了,最后我准备了一周去京东面试,一顿狂轰乱炸。

面试了一个下午,终于好了,以前我也没想过能够进去,总之,作为应届生,诚实一点(如果你的撒谎能力很X,至少我不行,一下就被揭穿了)谦虚一点,肯学肯吃苦,喜欢钻研,像头说的,可能现在多拿1k,可是5年后你拿的不是1k的问题了。

有些时候目光要长远一些。

企业不是慈善机构,学校都那样。企业愿意培养你,肯定觉得你有培养的价值。

android 4源码发布了,唉这几天没心情研究源码了,现在开始下载。嘿嘿。

应届生们,加油了,我们年轻,多面试几次就行了,很多不是我们不行,只是很多我们不知道,再次谢谢各个给我面试机会的公司,谢谢各位给我提建议的面试官。也谢谢把我简历DL的HR们。

自从上大学没有过过一次圣诞节,每次圣诞都考试,烦人,这次圣诞好好放松一下吧,好好努力


android面试360经验


今天去传说中高大上的360面试了,本人原来从事j2ee的web网站开发,自学android一年,今天被打击了,题目如下仅供参考,这种大公司都是看面试官临时发挥问问题啦~~

深刻醒悟到自己对安卓的理解是多么的浅薄~~

1 activity启动模式及应用场景:重点是问singleInstance的模式下是否新启动一个task,别的应用启动这个activity在哪个task里以及跳转到别的页面如何跳转回来?

其实这个问题,论坛里有很多帖子来讲一些理论的东西啦,当然也有很多错误的,大家不能看一面之词啦,还是去官网寻找答案。(面试官面无表情,我也就说了一些我自己的想法)

设置了"singleTask"启动模式的Activity的特点: 1. 设置了"singleTask"启动模式的Activity,它在启动的时候,会先在系统中查找属性值affinity等于它的属性值 taskAffinity的任务存在;如果存在这样的任务,它就会在这个任务中启动,否则就会在新任务中启动。因此,如果我们想要设置了"singleTask"启动模式的Activity在新的任务中启动,就要为它设置一个独立的taskAffinity属性值。 2. 如果设置了"singleTask"启动模式的Activity不是在新的任务中启动时,它会在已有的任务中查看是否已经存在相应的Activity实例,如果存在,就会把位于这个Activity实例上面的Activity全部结束掉,即最终这个Activity实例会位于任务的堆栈顶端中。

加一句感觉面试真得很像相亲,有缘分怎么都聊的来,没缘分说什么感觉都交流不了,我说了上面一大段singleTask的东西后,面试官问我 singleInstance的东西,还问另一个应用程序要使用singleInstance的页面,这个activity会在哪个栈里,感觉他应该问的是这个图的情况,但是他说是singleInstance,我就说singleInstance独占一个task了。2 然后问了Dalvik虚拟机和JVM虚拟机的区别,问的程度很深,单单说处理的东西不同,运行效率不同,Dalvik多个实例运行,面试官并不满意,追问了为什么基于寄存器就快,为什么Dalvik设计成多个实例运行这个问题确实把我难住了,一直研究应用开发确实忽略了研究Dalvik虚拟机。下面是找了一些答案给亲们做参考吧~特别提示,此题千万别说基于寄存器的Dalvik比基于堆栈的jvm运行速度快,我就是这样掉坑里了,又解释不出来,悲剧了。 Dalvik虚拟机与Java虚拟机的最显著区别是它们分别具有不同的类文件格式以及指令集。Dalvik虚拟机使用的是dex(Dalvik Executable)格式的类文件,而Java虚拟机使用的是class格式的类文件。一个dex文件可以包含若干个类,而一个class文件只包括一个类。由于一个dex文件可以包含若干个类,因此它就可以将各个类中重复的字符串和其它常数只保存一次,从而节省了空间,这样就适合在内存和处理器速度有限的手机系统中使用。一般来说,包含有相同类的未压缩dex文件稍小于一个已经压缩的jar文件。Dalvik虚拟机使用的指令是基于寄存器的,而 Java虚拟机使用的指令集是基于堆栈的。基于堆栈的指令很紧凑,例如,Java虚拟机使用的指令只占一个字节,因而称为字节码。基于寄存器的指令由于需要指定源地址和目标地址,因此需要占用更多的指令空间,例如,Dalvik虚拟机的某些指令需要占用两个字节。基于堆栈和基于寄存器的指令集各有优劣,一般而言,执行同样的功能,前者需要更多的指令(主要是load和store指令),而后者需要更多的指令空间。需要更多指令意味着要多占用CPU时间,而需要更多指令空间意味着数据缓冲(d-cache)更易失效。3 然后问了项目的成就感,我说了Oauth编程被鄙视,说这是业务不能算技术;说了DeviceplocyManager,反正没什么亮点吧。各位亲们各自发挥吧,最好是能抓住面试官的兴趣。4 消息推送的时候怎么获取每台设备的id?这题完全没答上来不知道什么意思。不过我说了开发过程中用过的Mac地址加上终端编号加上CPU的序列号,android不太了解应该有相应的东西。5进程间通信为什么用aidl,socket通信,content provider不是都可以进程间通信吗?6 为什么使用 content provider 共享数据?7 listview的优化,问了item内部的recycle问题,把我问住了总体而言,这次面试感觉非常不好,主要是经常会被面试官打断再深入问另一个问题思路经常会断,好吧,自己确实没有深入的去想android系统的设计理念和实现方式。

只能给亲们提供这些东西了,亲们加油吧!


本文我们来分享一批Android开发中最常用的组件下载地址及使用说明,都是开源免费的,绝对的干货。

了解常见的开源项目,可以扩大我们的视野,知道有哪些可以利用的资源,对于我们平常的设计和开发很有好处

UI相关

  • 图片

    • ps://github.com/nostra13/Android-Universal-Image-Loader" target="_blank">Android-Universal-Image-Loader:com.nostra13.universalimageloader:异步加载、缓存、显示图片

    • ImageLoader:com.novoda.imageloader:异步加载、缓存、显示图片

    • picasso:com.squareup.picasso:功能强大的图片下载缓存库

    • PhotoView:uk\co\senab\photoview:支持缩放和各种手势的ImageView

  • ListView

    • JazzyListView:com.twotoasters.jazzylistview:扩展的ListView,当列表项目在屏幕上可见时产生动画效果

    • StickyListHeaders:com.emilsjolander.components.stickylistheaders:在ListView中置顶

    • ListViewAnimations:com.haarman.listviewanimations:带动画的ListView

    • drag-sort-listview:拖拽排序ListView的元素

    • android-swipelistview:让listview的item可以向右滑动

  • 下拉刷新

    • Android-PullToRefresh:com.handmark.pulltorefresh:下拉刷新组件

    • android-pulltorefresh:下拉刷新组件

  • 菜单

    • SlidingMenu:com.jeremyfeinstein.slidingmenu:滑动菜单

    • MenuDrawer:滑动菜单组件

  • Action Bar

    • ActionBarSherlock:com.actionbarsherlock:Action Bar组件

    • android-actionbar:Action Bar组件

    • GlassActionBar:玻璃效果的Action Bar

  • ViewPager

    • Android-ViewPagerIndicator:com.viewpagerindicator:分页显示组件

    • PagerSlidingTabStrip:com.astuetz.viewpager:页面滑动组件

    • JazzyViewPager:可自定义动画的ViewPager

  • 兼容

    • NineOldAndroids:com.nineoldandroids:移植Honeycomb版本的动画API到旧版本上

    • HoloEverywhere:移植Android 4.1的Holo主题到旧的版本上

    • GlowPadBackport:GlowPadBackport:移植Android 4.2 GlowPadView到旧版本上

    • android-switch-backport::移植Android 4的Switch widget到旧版本上

  • AChartEngine:org.achartengine:Android上的绘图库

  • android-viewflow:com.taptwo.android.widget:视图切换的效果

  • android-flip:翻页动画组件

  • Android-AppMsg::In-layout notifications

  • android-wheel:kankan.wheel:Android滚动控件

  • Android-ProgressFragment:等待数据的时候,支持显示等待符号的Fragment控件

  • StaggeredGridView:瀑布流GridView布局

  • Cards-UI:卡片布局

  • cardslib:卡片布局

WebApp

  • Cordova:org.apache.cordova:Cordova是PhoneGap贡献给Apache后的开源项目,是从PhoneGap中抽出的核心代码

  • HtmlSpanner:net.nightwhistler.htmlspanner:Android上的网页渲染库,可渲染CSS

  • ChromeView:Chrome内核移植的WebView

推送

  • 个推:com.igexin:手机推送服务

  • JPush:???:极光推送

  • 百度推送:com.baidu.android.pushservice:百度推送服务

  • MQTT:ibm.mqtt:MQTT协议,似乎和推送有关系

语音识别

  • 讯飞SDK:com.iflytek:科大讯飞语音SDK

  • 百度语音识别:com.baidu.voicerecognition:百度语音识别SDK

  • mobvoi:com.mobvoi:移动语音搜索

  • 云知声:cn.yunzhisheng:云知声语音处理

音频视频图像

  • CC视频:com.bokecc:视频云平台

  • Vitamio:io.vov.vitamio:多媒体开发框架

  • leptonica:com.googlecode.leptonica:图像处理库

  • tesseract-ocr:com.googlecode.tesseract:图像OCR库

  • aacdecoder-android:com.spoledge.aacdecoder:Android上的Audio (AAC) 解码器

地图定位

  • 百度定位:com.baidu.location:百度地图SDK

  • 百度地图:com.baidu.mapapi:百度地图SDK

  • amap:com.amap.api,com.autonavi:高德地图API

  • 图吧SDK:com.mapbar:图吧地图API

  • MapABC:com.mapabc:MapABC地图SDK

广告平台

  • 友盟SDK:com.umeng:友盟统计、自动更新、用户反馈、社会化组件

  • 多盟:cn.domob:多盟平台

  • 百度移动联盟:com.baidu.mobads:百度移动联盟

  • google ads:com.google.ads:google广告

  • AdChina:com.adchina:易传媒广告平台

  • AdsMogo:com.adsmogo:芒果移动广告平台

  • Adwo:com.adwo:安沃移动广告平台

  • mobisage:com.mobisage:艾德思奇移动广告平台

  • Miaozhen:com.miaozhen:秒针第三方广告平台

  • AdMaster:cn.com.admaster:admaster广告平台

  • 易积分:com.qiang.escore:易积分移动广告平台

  • inmobi:com.inmobi:国外的广告平台

  • 点信传媒:cn.dx:广告平台

统计分析

  • Flurry:com.flurry:国外流行的统计工具

  • 百度移动统计:com.baidu.mobstat:百度开发者中心

  • Cobub Razor:com.wbtech.ums:移动统计分析工具

  • google analytics:com.google.analytics:google统计

  • lotuseed:com.lotuseed:莲子统计

  • Localytics:com.localytics.android:国外统计分析工具

  • comscore:com.comscore:国外的统计工具

网络通信

  • volley:com.android.volley:Android网络通信库

  • Apache Thrift:com.apache.thrift:远程服务调用框架

  • Netty:org.jboss.netty:异步事件驱动的网络应用程序框架

Http访问

  • Apache HttpClient:org.apache.http

  • android-async-http:com.loopj:异步Http库

  • async-http-client:异步Http和WebSocket库

  • OkHttp:实现了Google开发的SPDY协议,更快的网络传输和加载速度

XMPP协议

  • smack:org.jivesoftware.smack:XMPP客户端类库

  • Jbosh:com.kenai.jbosh:XMPP BOSH规范的Java实现

应用授权

  • Scribe:org.scribe:简单的OAuth认证

  • QQ互联:com.tencent.tauth:QQ互联

  • 百度授权:com.baidu.oauth:百度应用授权

  • weibo授权:com.sina.sso:新浪微博应用授权

社交分享

  • ShareSDK:cn.sharesdk:App分享库

  • facebook-android-sdk:com.facebook:Facebook SDK

  • 腾讯微信:com.tencent.mm:腾讯微信SDK

  • 腾讯微博:com.tencent.weibo:腾讯微博SDK

  • weiboSDK:com.weibo.sdk:新浪微博SDK

  • qweibo:com.mime.qweibo:Q版微博

  • t4j:t4j:网易微博开放平台

  • yixin:im.yixin:易信开放平台

  • 人人SDK:com.renren.api:人人网SDK

  • 翼聊:com.yiliao.android:中国电信天翼开放平台

  • evernote:com.evernote:Evernote API

  • 有道云笔记SDK:com.youdao.note:有道云笔记SDK

移动支付

  • alipay:com.alipay:支付宝

  • tenpay:com.tenpay:QQ财付通

  • umpay:com.umpay:联动优势支付平台

  • 银联支付:com.unionpay:中国银联手机支付平台

  • MMBilling:mm.purchasesdk:中国移动应用内计费SDK

Data解析

  • dom4j:org.dom4j:XML解析库

  • xmlpull:org.xmlpull.v1:XML解析器,Android自带

  • FastJSON:com.alibaba.fastjson:JSON解析器

  • Sparta:com.hp.hpl.sparta:XML、DOM、XPath解析器

  • jsoup:org.jsoup:HTML解析器

  • osbcp-css-parser:com.osbcp.cssparser:CSS解析器

  • HtmlCleaner:org.htmlcleaner:Html清洗解析库

  • Mime4J:org.apache.james.mime4j:MIME邮件格式解析器

序列化

  • google-gson:com.google.gson:序列化反序列化Java对象成Json数据

  • Jackson:org.codehaus.jackson:序列化反序列化Java对象成Json数据

ORM

  • OrmLite:com.j256.ormlite:Java ORM库

  • greenDAO:Android ORM for SQLite

  • AndrOrm:An ORM for Android

网盘

  • PCS:com.baidu.pcs:百度个人云存储

  • vdisk:com.vdisk:微盘开放平台

  • 金山快盘:com.kuaipan:金山快盘开放平台

异常收集分析

  • acra:org.acra:Application Crash Reports for Android

  • Crittercism:com.crittercism:为开发者提供分析诊断应用崩溃的原因

服务器

  • SwiFTP:org.swiftp:Android平台的FTP服务器

  • android-webserver:com.bolutions.webserver:Android平台的Web服务器

Event Bus

  • EventBus:de.greenrobot.event:an Android optimized publish/subscribe event bus

  • otto:基于Guav的Event Bus

Dependency Injection

  • RoboGuice:roboguice:Android平台的Dependency Injection框架

  • roboguice-sherlock:com.github.rtyley:使用RoboGuice实现的ActionBarSherlock

  • Google Guice:com.google.inject:Dependency Injection框架

图标资源

  • Androton-Action-Bar-Icons:一个针对Android 优化过的ICON图标集

  • http://iconsparadise.com/

  • http://iconbench.com/

  • http://www.androidicons.com/

  • https://code.google.com/p/android-ui-utils/

其他组件

  • android-query:com.androidquery:异步任务和操作UI元素

  • ZXing:com.google.zxing:条形码和二维码生成和解码库

  • pinyin4j:net.sourceforge.pinyin4j:中文和拼音转换

  • protobuf:com.google.protobuf:protobuf

  • JZlib:com.jcraft.jzlib:Java实现的zlib库

  • zt-zip:压缩解压库

  • aFileChooser:???:文件浏览器

  • image-chooser-library:???:图片和视频的选择库

  • TOML::跨语言的配置信息存取方案

  • OpenUDID:org.openudid:通用且持久的Unique Device IDentifier (UDID)解决方案

  • Parse:com.parse:各种很棒的后台服务

  • Codec:org.apache.codec:字符串编码解码库

  • jChardet:org.mozilla.intl.chardet:自动检测字符集

  • JRegex:jregex:正则表达式库

  • SQLCipher:info.guardianproject.database:Android数据库加密

  • xiaomi:com.xiaomi:小米开发者平台:推送服务、自动更新、自动发布等

  • DataDroid::以RESTful方式管理数据

  • Afinal::SQLITE的ORM和IOC框架,同时封装了android中的http框架

  • AndroidCommon:Android常用的一些库和功能,如缓存,下拉列表,下载管理,静默安装等

  • ThinkAndroid::Android整体框架:集成了ioc,orm,下载,缓存等模块,能让开发更加快速和高效

不常用组件

  • dnsjava:org.xbill.dns:域名解析

  • sasl:com.novell.sasl.client:sasl认证机制

  • LuaJava:org.keplerproject.luajava:Java嵌入Lua

  • PJSIP:org.pjsip.pjsua:PJSUA是一个开源的命令行SIP用户代理(软电话),用PJSIP协议,PJNATH,和PJMEDIA实现




UI框架

  • GreenDroid

  • Bearded-Hen/Android-Bootstrap

  • donnfelker/android-bootstrap

游戏引擎

  • cocos2d-x

  • libgdx

  • AndEngine

  • MonoGame

其他组件

  • skrollr:视差滚动Javascript引擎

  • androidannotations:扩展Android注解语言

  • android_guides:学习Android和iOs

  • phonegap:WebApp开发引擎


分本文来分享详细的在Android开发中,如何利用WebView与原生JS的数据交互,本教程附有代码和效果图,是一个不可多得的好教程。

关于WebView

我们知道目前android市场上的一些应用采用的开发方式大致分为三种:Native App、Web App、Hybrid App。本文主要是Hybrid App中实现的主要技术native组件与js的数据交互的理解以及实现。

Android API中提供了WebView组件来实现对html的渲染。所谓的HybridApp开发方式即是汇集了HTML5、CSS3、jS的相关开发技术,以及数据交换格式json/XML。这显然是Web开发工程师的技能。正是因为如此,众多中小企业选择了这种开发方式来减少对android开发工程师的过度依赖,至于这三种开发方式的比较与优劣不在本文考虑之列。

有了WebView这个组件,Android应用开发技术也就转嫁到html与java数据交互上来。说白了就是js与WebView的数据交互,这就是本文所要讨论的。


WebView与js的数据交互

1.        WebView中载入静态页面

 
将WebView添加到应用中。和原生控件一样,在layout引入WebView控件。代码片段如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/linearLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#000"
    android:orientation="horizontal" >
<WebView
    android:id="@+id/webview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    />
</LinearLayout>

载入页面:

 

webView = (WebView) findViewById(R.id.webview);
webView.loadUrl("file:///file:///android_asset/page.html");

page.html存储在工程文件的assets根目录下。

2.        引入jquery mobile

引入js框架让我们编写的html页面更接近于原生控件的显示效果。目前主流的移动应用js框架有:jquery mobile和sencha touch(jquery mobile与sencha touch的选型不在本文讨论范围)。本文选择使用jquery mobile。

 

首先,在webview添加对js的支持:

WebSettings setting = webView.getSettings();
setting.setJavaScriptEnabled(true);//支持js

增加对中文的支持:

WebSettings setting = webView.getSettings();
setting.setDefaultTextEncodingName("GBK");//设置字符编码

设置页面滚动条风格:

webView.setScrollBarStyle(0);//滚动条风格,为0指滚动条不占用空间,直接覆盖在网页上

jquery mobile提供的标准页面模板TemplateForJQuery.html:

<!DOCTYPE html>
<html>
    <head>
    <title>Page Title</title>
    
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <link rel="stylesheet" href="css/jquery.mobile-1.1.1.min.css" />
    <script src="js/jquery.js"></script>
    <script src="js/jquery.mobile-1.1.1.min.js"></script>
</head>
<body>

<div data-role="page">

    <div data-role="header">
        <h1>Page Title</h1>
    </div><!-- /header -->

    <div data-role="content">    
        <p>Page content goes here.</p>        
    </div><!-- /content -->

    <div data-role="footer">
        <h4>Page Footer</h4>
    </div><!-- /footer -->
</div><!-- /page -->

</body>
</html>

页面依赖的js库、css等均放在assets目录下,目录组织结构如下:



运行应用后的截图:


下面是button 的截图,与原生控件没什么明显区别,有种以假乱真的感觉:


3.        良好的用户体验

运行我们的应用发现,在拥有大量js的页面被载入时,一直处于等待中,这是很糟糕的用户体验。可以加入进度条解决。注意到webview提供的两个方法:setWebViewClient和setWebChromeClient。其中setWebChromeClient方法正是可以处理progress的加载,此外,还可以处理js对话框,在webview中显示icon图标等。对于处理progress的代码片段如下:

webView.setWebChromeClient(new WebChromeClient() {
    public void onProgressChanged(WebView view, int progress) {// 载入进度改变而触发
            if (progress == 100) {
                    handler.sendEmptyMessage(1);// 如果全部载入,隐藏进度对话框
            }
                super.onProgressChanged(view, progress);
        }
});

其中通过handler 消息机制来处理UI线程的更新:

 

        handler = new Handler() {
            public void handleMessage(Message msg) {// 定义一个Handler,用于处理下载线程与UI间通讯
                if (!Thread.currentThread().isInterrupted()){
                    switch (msg.what) {
                    case 0:
                        pd.show();// 显示进度对话框
                        break;
                    case 1:
                        pd.hide();// 隐藏进度对话框,不可使用dismiss()、cancel(),否则再次调用show()时,显示的对话框小圆圈不会动。
                        break;
                    }
                }
                super.handleMessage(msg);
            }
        };

对于setWebViewClient方法,一般用来处理html的加载(需要重载onPageStarted(WebView view, String url, Bitmap favicon))、关闭(需要重载onPageFinished(WebViewview, String url)方法)。

 

setWebViewClient和setWebChromeClient的作用:前者主要用于处理webView的控制问题,如加载、关闭、错误处理等;后者主要处理js对话框、图标、页面标题等。

4.        获取java中的数据

单独构建一个接口,作为处理js与java的数据交互的桥梁,本文封装的代码AndroidToastForJs.java如下:

public class AndroidToastForJs {
    
    private Context mContext;

public AndroidToastForJs(Context context){
        this.mContext = context;
    }
    
//webview中调用toast原生组件
public void showToast(String toast) {
        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
    }
    
//webview中求和
public int sum(int a,int b){
        return a+b;
    }
    
 //以json实现webview与js之间的数据交互
public String jsontohtml(){
        JSONObject map;
        JSONArray array = new JSONArray();
        try {
            map = new JSONObject();
            map.put("name","aaron");
            map.put("age", 25);
            map.put("address", "中国上海");
            array.put(map);
            
            map = new JSONObject();
            map.put("name","jacky");
            map.put("age", 22);
            map.put("address", "中国北京");
            array.put(map);
            
            map = new JSONObject();
            map.put("name","vans");
            map.put("age", 26);
            map.put("address", "中国深圳");
            map.put("phone","13888888888");
            array.put(map);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return array.toString();
    }
}


 

Webview提供的传入js的方法:

webView.addJavascriptInterface(new AndroidToastForJs(mContext), "JavaScriptInterface");

Html页面jsonData.html设计的部分代码如下:

    <script type="text/javascript">
    var result = JavaScriptInterface.jsontohtml();
    var obj = eval("("+result+")");//解析json字符串
    function showAndroidToast(toast)
    {        
        JavaScriptInterface.showToast(toast);
    }
    function getjsonData(){
        var result = JavaScriptInterface.jsontohtml();
        var obj = eval("("+result+")");//解析json字符串
        for(i=0;i<obj.length;i++){
            var user=obj[i];
            document.write("<p>姓名:"+user.name+"</p>");
            document.write("<p>年龄:"+user.age+"</p>");
            document.write("<p>地址:"+user.address+"</p>");
            if(user.phone!=null){
                document.write("<p>手机号码:"+user.address+"</p>");
            }
        }
    }    
    function list(){
        document.write("<div data-role='header'><p>another</p></div>");
    }
    </script>
</head>
<body>
<div data-role="page" >
    <div data-role="header" data-theme="c">
        <h1>Android via Interface</h1>
    </div><!-- /header -->
    <div data-role="content">    
        <button value="say hello" onclick="showAndroidToast('Hello,Android!')" data-theme="e"></button>
        <button value="get json data" onclick="getjsonData()" data-theme="e"></button>    
    </div><!-- /content -->
<div data-role="collapsible" data-theme="c" data-content-theme="f">
   <h3>I'm <script>document.write(obj[0].name);</script>,click to see my info</h3>
   <p><script>document.write("<p>姓名:"+obj[0].name+"</p>");</script></p>
   <p><script>document.write("<p>年龄:"+obj[0].age+"</p>");</script></p>
   <p><script>document.write("<p>地址:"+obj[0].address+"</p>");</script></p>
</div>
    <div data-role="footer" data-theme="c">
        <h4>Page Footer</h4>
    </div><!-- /footer -->
</div><!-- /page -->
</body>

点击say hello按钮运行的截图如下:




另外一篇关于webview与js交互

对于android初学者应该都了解webView这个组件。之前我也是对其进行了一些简单的了解,但是在一个项目中不得不用webview的时候,发现了webview的强大之处,今天就分享一下使用webview的一些经验。

 
1、首先了解一下webview。

webview介绍的原文如下:A View that displays web pages. This class is the basis upon which you can roll your own web browser or simply display some online content within your Activity. It uses the WebKit rendering engine to display web pages and includes methods to navigate forward and backward through a history, zoom in and out, perform text searches and more.

从上面你应该了解到了基本功能,也就是显示网页。之所以我说webview功能强大是因为它和js的交互非常方便,很简单就可以实现。

 

2、webview能做什么?

①webView可以利用html做界面布局,虽然目前还比较少人这么使用,不过我相信当一些客户端需要复杂的图文(图文都是动态生成)混排的时候它肯定是个不错的选择。

②直接显示网页,这功能当然也是它最基本的功能。

③和js交互。(如果你的js基础比java基础好的话那么采用这种方式做一些复杂的处理是个不错的选择)。

 

3、如何使用webview?

这里直接用一个svn上取下的demo,先上demo后讲解。demo的结构图如下:

 
WebViewDemo.java

package com.google.android.webviewdemo;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;

/**
 * Demonstrates how to embed a WebView in your activity. Also demonstrates how
 * to have javascript in the WebView call into the activity, and how the activity
 * can invoke javascript.
 * <p>
 * In this example, clicking on the android in the WebView will result in a call into
 * the activities code in {@link DemoJavaScriptInterface#clickOnAndroid()}. This code
 * will turn around and invoke javascript using the {@link WebView#loadUrl(String)}
 * method.
 * <p>
 * Obviously all of this could have been accomplished without calling into the activity
 * and then back into javascript, but this code is intended to show how to set up the
 * code paths for this sort of communication.
 *
 */
public class WebViewDemo extends Activity {

    private static final String LOG_TAG = "WebViewDemo";

    private WebView mWebView;

    private Handler mHandler = new Handler();

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.main);
        mWebView = (WebView) findViewById(R.id.webview);

        WebSettings webSettings = mWebView.getSettings();
        webSettings.setSavePassword(false);
        webSettings.setSaveFormData(false);
        webSettings.setJavaScriptEnabled(true);
        webSettings.setSupportZoom(false);

        mWebView.setWebChromeClient(new MyWebChromeClient());

        mWebView.addJavascriptInterface(new DemoJavaScriptInterface(), "demo");

        mWebView.loadUrl("file:///android_asset/demo.html");
    }

    final class DemoJavaScriptInterface {

        DemoJavaScriptInterface() {
        }

        /**
         * This is not called on the UI thread. Post a runnable to invoke
         * loadUrl on the UI thread.
         */
        public void clickOnAndroid() {
            mHandler.post(new Runnable() {
                public void run() {
                    mWebView.loadUrl("javascript:wave()");
                }
            });

        }
    }

    /**
     * Provides a hook for calling "alert" from javascript. Useful for
     * debugging your javascript.
     */
    final class MyWebChromeClient extends WebChromeClient {
        @Override
        public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
            Log.d(LOG_TAG, message);
            result.confirm();
            return true;
        }
    }
}

 

demo.html

<html>
    <script language="javascript">
        /* This function is invoked by the activity */
        function wave() {
            alert("1");
            document.getElementById("droid").src="android_waving.png";
            alert("2");
        }
    </script>
    <body>
        <!-- Calls into the javascript interface for the activity -->
        <a onClick="window.demo.clickOnAndroid()"><div style="width:80px;
            margin:0px auto;
            padding:10px;
            text-align:center;
            border:2px solid #202020;" >
                <img id="droid" src="android_normal.png"/><br>
                Click me!
        </div></a>
    </body>
</html>

 

main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    
    <TextView  
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/intro"
        android:padding="4dip"
        android:textSize="16sp"
        />
    
    <WebView
        android:id="@+id/webview"
        android:layout_width="fill_parent"
        android:layout_height="0dip"
        android:layout_weight="1"
        />
        
</LinearLayout>

 

4、如何交互?

①android如何调用js。

调用 形式:

mWebView.loadUrl("javascript:wave()");

其中wave()是js中的一个方法,当然你可以把这个方法改成其他的方法,也就是android调用其他的方法。

②js如何调用android。

调用形式:

<a onClick="window.demo.clickOnAndroid()">

代码中的“demo”是在android中指定的调用名称,即

 mWebView.addJavascriptInterface(new DemoJavaScriptInterface(), "demo");

代码中的clickOnAndroid()是“demo”对应的对象:new DemoJavaScriptInterface() 中的一个方法。

③双向交互。

当然是把前面的两种方式组合一下就可以了。

 

5、讲解demo。

现在你一定了解了android和js的交互了。是时候分析一些demo了,根据上面讲的你也应该比较清楚了。具体交互流程如下:

①点击图片,则在js端直接调用android上的方法clickOnAndroid();

②clickOnAndroid()方法(利用线程)调用js的方法。

③被②调用的js直接控制html。

 

个人总结:利用webView的这种方式在有些时候UI布局就可以转成相应的html代码编写了,而html布局样式之类有DW这样强大的工具,而且网上很多源码,很多代码片。在UI和视觉效果上就会节省很多时间,重复发明轮子没有任何意义。

[!--infotagslink--]

相关文章

  • 安卓手机app添加支付宝支付开发教程

    支付宝支付在国内算是大家了,我们到处都可以使用支付宝了,下文整理介绍的是在安卓app应用中使用支付宝进行支付的开发例子。 之前讲了一篇博客关与支付宝集成获取...2016-09-20
  • PHP成员变量获取对比(类成员变量)

    下面本文章来给大家介绍在php中成员变量的一些对比了,文章举了四个例子在这例子中分别对不同成员变量进行测试与获取操作,下面一起来看看。 有如下4个代码示例,你认...2016-11-25
  • PHP+Ajax手机发红包的程序例子

    PHP+Ajax有许多的功能都会用到它小编今天就有使用PHP+Ajax实现的一个微信登录功能了,下面我们来看一个PHP+Ajax手机发红包的程序例子,具体如下所示。 PHP发红包基本...2016-11-25
  • php 获取用户IP与IE信息程序

    php 获取用户IP与IE信息程序 function onlineip() { global $_SERVER; if(getenv('HTTP_CLIENT_IP')) { $onlineip = getenv('HTTP_CLIENT_IP');...2016-11-25
  • php获取一个文件夹的mtime的程序

    php获取一个文件夹的mtime的程序了,这个就是时间问题了,对于这个问题我们来看小编整理的几个例子,具体的操作例子如下所示。 php很容易获取到一个文件夹的mtime,可以...2016-11-25
  • Android子控件超出父控件的范围显示出来方法

    下面我们来看一篇关于Android子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。 <RelativeLayout xmlns:an...2016-10-02
  • 华为手机怎么恢复已卸载的应用?华为手机恢复应用教程

    华为手机怎么恢复已卸载的应用?有时候我们在使用华为手机的时候,想知道卸载的应用怎么恢复,这篇文章主要介绍了华为手机恢复应用教程,需要的朋友可以参考下...2020-06-29
  • 如何获取网站icon有哪些可行的方法

    获取网站icon,常用最简单的方法就是通过website/favicon.ico来获取,不过由于很多网站都是在页面里面设置favicon,所以此方法很多情况都不可用。 更好的办法是通过google提供的服务来实现:http://www.google.com/s2/favi...2014-06-07
  • 破解安卓手机上的九宫格锁方法步骤

    很多用安卓智能手机的朋友是用九宫格锁屏,网上也有暴力删除手机图形锁的方法,不过我们可以用程序来破解。本文只提供技术学习,不能干坏事 安卓手机的图形锁(九宫格)...2016-09-20
  • jquery如何获取元素的滚动条高度等实现代码

    主要功能:获取浏览器显示区域(可视区域)的高度 : $(window).height(); 获取浏览器显示区域(可视区域)的宽度 :$(window).width(); 获取页面的文档高度 $(document).height(); 获取页面的文档宽度 :$(document).width();...2015-10-21
  • 华为手机怎么开启双时钟? 华为添加双时钟的技巧

    华为手机怎么开启双时钟?华为手机是可以设置双时钟的,如果来回在两个有时差的地方工作,是可以设置双时钟显示,下面我们就来看看华为添加双时钟的技巧,需要的朋友可以参考下...2020-12-08
  • 安卓手机wifi打不开修复教程,安卓手机wifi打不开解决方法

    手机wifi打不开?让小编来告诉你如何解决。还不知道的朋友快来看看。 手机wifi是现在生活中最常用的手机功能,但是遇到手机wifi打不开的情况该怎么办呢?如果手机wifi...2016-12-21
  • Android开发中findViewById()函数用法与简化

    findViewById方法在android开发中是获取页面控件的值了,有没有发现我们一个页面控件多了会反复研究写findViewById呢,下面我们一起来看它的简化方法。 Android中Fin...2016-09-20
  • Android模拟器上模拟来电和短信配置

    如果我们的项目需要做来电及短信的功能,那么我们就得在Android模拟器开发这些功能,本来就来告诉我们如何在Android模拟器上模拟来电及来短信的功能。 在Android模拟...2016-09-20
  • jquery获取div距离窗口和父级dv的距离示例

    jquery中jquery.offset().top / left用于获取div距离窗口的距离,jquery.position().top / left 用于获取距离父级div的距离(必须是绝对定位的div)。 (1)先介绍jquery.offset().top / left css: 复制代码 代码如下: *{ mar...2013-10-13
  • 夜神android模拟器设置代理的方法

    夜神android模拟器如何设置代理呢?对于这个问题其实操作起来是非常的简单,下面小编来为各位详细介绍夜神android模拟器设置代理的方法,希望例子能够帮助到各位。 app...2016-09-20
  • Jquery 获取指定标签的对象及属性的设置与移除

    1、先讲讲JQuery的概念,JQuery首先是由一个 America 的叫什么 John Resig的人创建的,后来又很多的JS高手也加入了这个团队。其实 JQuery是一个JavaScript的类库,这个类库集合了很多功能方法,利用类库你可以用简单的一些代...2014-05-31
  • android自定义动态设置Button样式【很常用】

    为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。 今天来看一个通过重写Button来动态实...2016-09-20
  • Android WebView加载html5页面实例教程

    如果我们要在Android应用APP中加载html5页面,我们可以使用WebView,本文我们分享两个WebView加载html5页面实例应用。 实例一:WebView加载html5实现炫酷引导页面大多...2016-09-20
  • 深入理解Android中View和ViewGroup

    深入理解Android中View和ViewGroup从组成架构上看,似乎ViewGroup在View之上,View需要继承ViewGroup,但实际上不是这样的。View是基类,ViewGroup是它的子类。本教程我们深...2016-09-20