Android Studio 下载视频到本地

转载  更新时间:2018年03月08日 10:48:42   作者:zpf_   我要评论

这篇文章主要介绍了Android Studio 下载视频到本地,利用GreenDao实现多线程断点续传,这样的话,下次用户再次下载时,将继续上次数据库的接着下载,这样用户体验就会很好,也大大节省了成本.具体实现代码大家参考下本文

最近在研究视频下载到本地的问题,像爱奇艺,腾讯视频,迅雷看看等等一些视频播放器,如果在一个播放器里面视频下载到一半用户退出App之后,再次登录从头开始,那么就太可悲了,所以在做视频音频类的项目时,要实现的一个功能就是断点续传,就是将用户下载的视频或者音频等以字节流的形式存入数据库,下次用户再次下载时,将继续上次数据库的接着下载,这样用户体验就会很好,也大大节省了成本.

好了废话不多说,开始今天的正题.

一、先上效果图

二、使用GreenDao我们需要导入依赖

1.以下在项目gradle依赖中添加

compile 'org.greenrobot:greendao:3.2.2'//依赖 在最后一行插入
apply plugin: 'org.greenrobot.greendao' //greenDao在第二行插入
greendao {//在依赖导入汇总添加一个自动添加数据库表名的配置依赖
 schemaVersion 1 //数据库版本号
 daoPackage 'com.example.greendaodemo.database' //设置时生成代码的目录
 targetGenDir 'src/main/java' //设置DaoMaster、DaoSession、Dao目录
}

2.以下在工程gradle依赖中添加

classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2'

3.既然我们要联网下载,读写权限肯定不能忘记了

<uses-permission android:name="android.permission.INTERNET"/> 
 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> 
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 
 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> 
 <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" /> 

三、当然还要有实体类写入实体类后,build下make project一下,让他自动生成GreenDao的三个类

import org.greenrobot.greendao.annotation.Entity; 
import org.greenrobot.greendao.annotation.Id; 
import org.greenrobot.greendao.annotation.Generated; 
/** 
 * author:Created by ZhangPengFei. 
 * Email: 1271396448@qq.com 
 * data: 2018/1/18 
 */ 
@Entity 
public class User { 
 @Id 
 private Long id; 
 private Integer thread_id; 
 private Integer start_pos; 
 private Integer end_pos; 
 private Integer compelete_size; 
 private String url; 
 @Generated(hash = 2041931179) 
 public User(Long id, Integer thread_id, Integer start_pos, Integer end_pos, 
   Integer compelete_size, String url) { 
  this.id = id; 
  this.thread_id = thread_id; 
  this.start_pos = start_pos; 
  this.end_pos = end_pos; 
  this.compelete_size = compelete_size; 
  this.url = url; 
 } 
 @Generated(hash = 586692638) 
 public User() { 
 } 
 public Long getId() { 
  return this.id; 
 } 
 public void setId(Long id) { 
  this.id = id; 
 } 
 public Integer getThread_id() { 
  return this.thread_id; 
 } 
 public void setThread_id(Integer thread_id) { 
  this.thread_id = thread_id; 
 } 
 public Integer getStart_pos() { 
  return this.start_pos; 
 } 
 public void setStart_pos(Integer start_pos) { 
  this.start_pos = start_pos; 
 } 
 public Integer getEnd_pos() { 
  return this.end_pos; 
 } 
 public void setEnd_pos(Integer end_pos) { 
  this.end_pos = end_pos; 
 } 
 public Integer getCompelete_size() { 
  return this.compelete_size; 
 } 
 public void setCompelete_size(Integer compelete_size) { 
  this.compelete_size = compelete_size; 
 } 
 public String getUrl() { 
  return this.url; 
 } 
 public void setUrl(String url) { 
  this.url = url; 
 } 
} 

四、本人使用了单例模式,所以在全局配置里初始化了数据库的操作

import android.app.Application; 
import com.example.greendaodemo.database.DaoMaster; 
import com.example.greendaodemo.database.DaoSession; 
import com.example.greendaodemo.database.UserDao; 
/** 
 * author:Created by ZhangPengFei. 
 * Email: 1271396448@qq.com 
 * data: 2018/1/18 
 */ 
public class App extends Application { 
 public static UserDao userDao; 
 @Override 
 public void onCreate() { 
  super.onCreate(); 
  DaoMaster.DevOpenHelper devOpenHelper = new DaoMaster.DevOpenHelper(this, "zpf.db", null); 
  DaoMaster daoMaster = new DaoMaster(devOpenHelper.getWritableDb()); 
  DaoSession daoSession = daoMaster.newSession(); 
  userDao = daoSession.getUserDao(); 
 } 
} 

五、因为我们是多线程,所以写一个保存每个线程下载信息的类

public class DownLoadInfo { 
 /** 
  * 保存每个下载线程下载信息类 
  * 
  */ 
 private int threadId;// 下载器id 
 private int startPos;// 开始点 
 private int endPos;// 结束点 
 private int compeleteSize;// 完成度 
 private String url;// 下载文件的URL地址 
 public DownLoadInfo(int threadId, int startPos, int endPos, 
      int compeleteSize, String url) { 
  this.threadId = threadId; 
  this.startPos = startPos; 
  this.endPos = endPos; 
  this.compeleteSize = compeleteSize; 
  this.url = url; 
 } 
 public DownLoadInfo() { 
 } 
 public String getUrl() { 
  return url; 
 } 
 public void setUrl(String url) { 
  this.url = url; 
 } 
 public int getThreadId() { 
  return threadId; 
 } 
 public void setThreadId(int threadId) { 
  this.threadId = threadId; 
 } 
 public int getStartPos() { 
  return startPos; 
 } 
 public void setStartPos(int startPos) { 
  this.startPos = startPos; 
 } 
 public int getEndPos() { 
  return endPos; 
 } 
 public void setEndPos(int endPos) { 
  this.endPos = endPos; 
 } 
 public int getCompeleteSize() { 
  return compeleteSize; 
 } 
 public void setCompeleteSize(int compeleteSize) { 
  this.compeleteSize = compeleteSize; 
 } 
 @Override 
 public String toString() { 
  return "DownloadInfo [threadId=" + threadId + ", startPos=" + startPos 
    + ", endPos=" + endPos + ", compeleteSize=" + compeleteSize 
    + "]"; 
 } 
} 

六、写一个用来记录的类,修改,添加和下载完清空数据库的操作

import android.util.Log; 
import com.example.greendaodemo.database.UserDao; 
import java.util.ArrayList; 
import java.util.List; 
import static com.bwie.mtd.App.userDao; 
/** 
 * author:Created by ZhangPengFei. 
 * Email: 1271396448@qq.com 
 * data: 2018/1/18 
 */ 
public class DownLoadSqlTool { 
 /** 
  * 创建下载的具体信息 
  */ 
 public void insertInfos(List<DownLoadInfo> infos) { 
  for (DownLoadInfo info : infos) { 
   User user = new User(null, info.getThreadId(), info.getStartPos(), info.getEndPos(), info.getCompeleteSize(), info.getUrl()); 
   userDao.insert(user); 
  } 
 } 
 /** 
  * 得到下载具体信息 
  */ 
 public List<DownLoadInfo> getInfos(String urlstr) { 
  List<DownLoadInfo> list = new ArrayList<DownLoadInfo>(); 
  List<User> list1 = userDao.queryBuilder().where(UserDao.Properties.Url.eq(urlstr)).build().list(); 
  for (User user : list1) { 
   DownLoadInfo infoss = new DownLoadInfo( 
     user.getThread_id(), user.getStart_pos(), user.getEnd_pos(), 
     user.getCompelete_size(), user.getUrl()); 
   Log.d("main-----", infoss.toString()); 
   list.add(infoss); 
  } 
  return list; 
 } 
 /** 
  * 更新数据库中的下载信息 
  */ 
 public void updataInfos(int threadId, int compeleteSize, String urlstr) { 
  User user = userDao.queryBuilder() 
    .where(UserDao.Properties.Thread_id.eq(threadId), UserDao.Properties.Url.eq(urlstr)).build().unique(); 
  user.setCompelete_size(compeleteSize); 
  userDao.update(user); 
 } 
 /** 
  * 下载完成后删除数据库中的数据 
  */ 
 public void delete(String url) { 
  userDao.deleteAll(); 
 } 
} 

七、多线程下载的实践类

import android.content.Context; 
import android.os.Handler; 
import android.os.Message; 
import android.util.Log; 
import java.io.File; 
import java.io.InputStream; 
import java.io.RandomAccessFile; 
import java.net.HttpURLConnection; 
import java.net.URL; 
import java.util.ArrayList; 
import java.util.List; 
/** 
 * author:Created by ZhangPengFei. 
 * Email: 1271396448@qq.com 
 * data: 2018/1/18 
 */ 
public class DownloadHttpTool { 
 /** 
  * 利用Http协议进行多线程下载具体实践类 
  */ 
 private static final String TAG = DownloadHttpTool.class.getSimpleName(); 
 private int threadCount;//线程数量 
 private String urlstr;//URL地址 
 private Context mContext; 
 private Handler mHandler; 
 private List<DownLoadInfo> downloadInfos;//保存下载信息的类 
 private String localPath;//目录 
 private String fileName;//文件名 
 private int fileSize; 
 private DownLoadSqlTool sqlTool;//文件信息保存的数据库操作类 
 private enum Download_State { 
  Downloading, Pause, Ready;//利用枚举表示下载的三种状态 
 } 
 private Download_State state = Download_State.Ready;//当前下载状态 
 private int globalCompelete = 0;//所有线程下载的总数 
 public DownloadHttpTool(int threadCount, String urlString, 
       String localPath, String fileName, Context context, Handler handler) { 
  super(); 
  this.threadCount = threadCount; 
  this.urlstr = urlString; 
  this.localPath = localPath; 
  this.mContext = context; 
  this.mHandler = handler; 
  this.fileName = fileName; 
  sqlTool = new DownLoadSqlTool(); 
 } 
 //在开始下载之前需要调用ready方法进行配置 
 public void ready() { 
  Log.w(TAG, "ready"); 
  globalCompelete = 0; 
  downloadInfos = sqlTool.getInfos(urlstr); 
  if (downloadInfos.size() == 0) { 
   initFirst(); 
  } else { 
   File file = new File(localPath + "/" + fileName); 
   if (!file.exists()) { 
    sqlTool.delete(urlstr); 
    initFirst(); 
   } else { 
    fileSize = downloadInfos.get(downloadInfos.size() - 1) 
      .getEndPos(); 
    for (DownLoadInfo info : downloadInfos) { 
     globalCompelete += info.getCompeleteSize(); 
    } 
    Log.w(TAG, "globalCompelete:::" + globalCompelete); 
   } 
  } 
 } 
 public void start() { 
  Log.w(TAG, "start"); 
  if (downloadInfos != null) { 
   if (state == Download_State.Downloading) { 
    return; 
   } 
   state = Download_State.Downloading; 
   for (DownLoadInfo info : downloadInfos) { 
    Log.v(TAG, "startThread"); 
    new DownloadThread(info.getThreadId(), info.getStartPos(), 
      info.getEndPos(), info.getCompeleteSize(), 
      info.getUrl()).start(); 
   } 
  } 
 } 
 public void pause() { 
  state = Download_State.Pause; 
 } 
 public void delete() { 
  compelete(); 
  File file = new File(localPath + "/" + fileName); 
  file.delete(); 
 } 
 public void compelete() { 
  sqlTool.delete(urlstr); 
 } 
 public int getFileSize() { 
  return fileSize; 
 } 
 public int getCompeleteSize() { 
  return globalCompelete; 
 } 
 //第一次下载初始化 
 private void initFirst() { 
  Log.w(TAG, "initFirst"); 
  try { 
   URL url = new URL(urlstr); 
   HttpURLConnection connection = (HttpURLConnection) url 
     .openConnection(); 
   connection.setConnectTimeout(5000); 
   connection.setRequestMethod("GET"); 
   fileSize = connection.getContentLength(); 
   Log.w(TAG, "fileSize::" + fileSize); 
   File fileParent = new File(localPath); 
   if (!fileParent.exists()) { 
    fileParent.mkdir(); 
   } 
   File file = new File(fileParent, fileName); 
   if (!file.exists()) { 
    file.createNewFile(); 
   } 
   // 本地访问文件 
   RandomAccessFile accessFile = new RandomAccessFile(file, "rwd"); 
   accessFile.setLength(fileSize); 
   accessFile.close(); 
   connection.disconnect(); 
  } catch (Exception e) { 
   e.printStackTrace(); 
  } 
  int range = fileSize / threadCount; 
  downloadInfos = new ArrayList<DownLoadInfo>(); 
  for (int i = 0; i < threadCount - 1; i++) { 
   DownLoadInfo info = new DownLoadInfo(i, i * range, (i + 1) * range 
     - 1, 0, urlstr); 
   downloadInfos.add(info); 
  } 
  DownLoadInfo info = new DownLoadInfo(threadCount - 1, (threadCount - 1) 
    * range, fileSize - 1, 0, urlstr); 
  downloadInfos.add(info); 
  sqlTool.insertInfos(downloadInfos); 
 } 
 //自定义下载线程 
 private class DownloadThread extends Thread { 
  private int threadId; 
  private int startPos; 
  private int endPos; 
  private int compeleteSize; 
  private String urlstr; 
  private int totalThreadSize; 
  public DownloadThread(int threadId, int startPos, int endPos, 
        int compeleteSize, String urlstr) { 
   this.threadId = threadId; 
   this.startPos = startPos; 
   this.endPos = endPos; 
   totalThreadSize = endPos - startPos + 1; 
   this.urlstr = urlstr; 
   this.compeleteSize = compeleteSize; 
  } 
  @Override 
  public void run() { 
   HttpURLConnection connection = null; 
   RandomAccessFile randomAccessFile = null; 
   InputStream is = null; 
   try { 
    randomAccessFile = new RandomAccessFile(localPath + "/" 
      + fileName, "rwd"); 
    randomAccessFile.seek(startPos + compeleteSize); 
    URL url = new URL(urlstr); 
    connection = (HttpURLConnection) url.openConnection(); 
    connection.setConnectTimeout(5000); 
    connection.setRequestMethod("GET"); 
    connection.setRequestProperty("Range", "bytes=" 
      + (startPos + compeleteSize) + "-" + endPos); 
    is = connection.getInputStream(); 
    byte[] buffer = new byte[1024]; 
    int length = -1; 
    while ((length = is.read(buffer)) != -1) { 
     randomAccessFile.write(buffer, 0, length); 
     compeleteSize += length; 
     Message message = Message.obtain(); 
     message.what = threadId; 
     message.obj = urlstr; 
     message.arg1 = length; 
     mHandler.sendMessage(message); 
     sqlTool.updataInfos(threadId, compeleteSize, urlstr); 
     Log.w(TAG, "Threadid::" + threadId + " compelete::" 
       + compeleteSize + " total::" + totalThreadSize); 
     if (compeleteSize >= totalThreadSize) { 
      break; 
     } 
     if (state != Download_State.Downloading) { 
      break; 
     } 
    } 
   } catch (Exception e) { 
    e.printStackTrace(); 
   } finally { 
    try { 
     if (is != null) { 
      is.close(); 
     } 
     randomAccessFile.close(); 
     connection.disconnect(); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
   } 
  } 
 } 
} 

八、再写一个接口类

import android.annotation.SuppressLint; 
import android.content.Context; 
import android.os.AsyncTask; 
import android.os.Handler; 
import android.os.Message; 
import android.util.Log; 
/** 
 * author:Created by ZhangPengFei. 
 * Email: 1271396448@qq.com 
 * data: 2018/1/18 
 */ 
public class DownloadUtil { 
 private DownloadHttpTool mDownloadHttpTool; 
 private OnDownloadListener onDownloadListener; 
 private int fileSize; 
 private int downloadedSize = 0; 
 @SuppressLint("HandlerLeak") 
 private Handler mHandler = new Handler() { 
  @Override 
  public void handleMessage(Message msg) { 
   // TODO Auto-generated method stub 
   super.handleMessage(msg); 
   int length = msg.arg1; 
   synchronized (this) {//加锁保证已下载的正确性 
    downloadedSize += length; 
   } 
   if (onDownloadListener != null) { 
    onDownloadListener.downloadProgress(downloadedSize); 
   } 
   if (downloadedSize >= fileSize) { 
    mDownloadHttpTool.compelete(); 
    if (onDownloadListener != null) { 
     onDownloadListener.downloadEnd(); 
    } 
   } 
  } 
 }; 
 public DownloadUtil(int threadCount, String filePath, String filename, 
      String urlString, Context context) { 
  mDownloadHttpTool = new DownloadHttpTool(threadCount, urlString, 
    filePath, filename, context, mHandler); 
 } 
 //下载之前首先异步线程调用ready方法获得文件大小信息,之后调用开始方法 
 public void start() { 
  new AsyncTask<Void,Void,Void>() { 
   @Override 
   protected Void doInBackground(Void... arg0) { 
    // TODO Auto-generated method stub 
    mDownloadHttpTool.ready(); 
    return null; 
   } 
   @Override 
   protected void onPostExecute(Void result) { 
    // TODO Auto-generated method stub 
    super.onPostExecute(result); 
    fileSize = mDownloadHttpTool.getFileSize(); 
    downloadedSize = mDownloadHttpTool.getCompeleteSize(); 
    Log.w("Tag", "downloadedSize::" + downloadedSize); 
    if (onDownloadListener != null) { 
     onDownloadListener.downloadStart(fileSize); 
    } 
    mDownloadHttpTool.start(); 
   } 
  }.execute(); 
 } 
 public void pause() { 
  mDownloadHttpTool.pause(); 
 } 
 public void delete(){ 
  mDownloadHttpTool.delete(); 
 } 
 public void reset(){ 
  mDownloadHttpTool.delete(); 
  start(); 
 } 
 public void setOnDownloadListener(OnDownloadListener onDownloadListener) { 
  this.onDownloadListener = onDownloadListener; 
 } 
 //下载回调接口 
 public interface OnDownloadListener { 
  public void downloadStart(int fileSize); 
  public void downloadProgress(int downloadedSize);//记录当前所有线程下总和 
  public void downloadEnd(); 
 } 
} 

九、写一下自己的布局文件

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
 xmlns:app="http://schemas.android.com/apk/res-auto" 
 xmlns:tools="http://schemas.android.com/tools" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 tools:context="com.mtd.MainActivity"> 
 <ProgressBar 
  android:id="@+id/progressBar" 
  style="?android:attr/progressBarStyleHorizontal" 
  android:layout_width="fill_parent" 
  android:layout_height="7.5dp" 
  android:layout_centerInParent="true" 
  android:layout_marginRight="8dp" 
  android:max="100" 
  android:progress="100" 
  android:visibility="visible" /> 
 <TextView 
  android:id="@+id/tv_Progress" 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:layout_below="@id/progressBar" 
  android:layout_centerHorizontal="true" 
  android:layout_marginLeft="23dp" 
  android:layout_marginStart="23dp" 
  android:layout_marginTop="18dp" 
  android:text="" /> 
 <Button 
  android:id="@+id/downLoad" 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:layout_alignParentBottom="true" 
  android:layout_marginLeft="100dp" 
  android:text="下载" /> 
 <Button 
  android:id="@+id/pause" 
  android:layout_toRightOf="@id/downLoad" 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:layout_alignParentBottom="true" 
  android:layout_marginLeft="48dp" 
  android:layout_marginStart="48dp" 
  android:text="暂停" /> 
</RelativeLayout> 

十、奉上自己的Java代码MainActivity类

import android.os.Bundle; 
import android.os.Environment; 
import android.support.v7.app.AppCompatActivity; 
import android.util.Log; 
import android.view.View; 
import android.widget.Button; 
import android.widget.ProgressBar; 
import android.widget.TextView; 
public class MainActivity extends AppCompatActivity { 
 private final String videoUrl = "http://2449.vod.myqcloud.com/2449_22ca37a6ea9011e5acaaf51d105342e3.f20.mp4"; 
 private TextView tv_progress;//进度显示 
 private ProgressBar progressBar;//进度条 
 private Button downLoad;//下载按钮 
 private Button pause;//暂停按钮 
 private String path;//下载路径 
 private int max; 
 @Override 
 protected void onCreate(Bundle savedInstanceState) { 
  super.onCreate(savedInstanceState); 
  setContentView(R.layout.activity_main); 
  tv_progress = (TextView) findViewById(R.id.tv_Progress); 
  progressBar = (ProgressBar) findViewById(R.id.progressBar); 
  downLoad = (Button) findViewById(R.id.downLoad); 
  pause = (Button) findViewById(R.id.pause); 
  path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/local"; 
  final DownloadUtil downloadUtil = new DownloadUtil(4, path, "drum.mp4", videoUrl, this); 
  downloadUtil.setOnDownloadListener(new DownloadUtil.OnDownloadListener() { 
   @Override 
   public void downloadStart(int fileSize) { 
    Log.i("TAG---fileSize", fileSize + ""); 
    max = fileSize;//文件总长度 
    progressBar.setMax(fileSize); 
   } 
   @Override 
   public void downloadProgress(int downloadedSize) { 
    Log.i("TAG---downloadedSize", downloadedSize + ""); 
    progressBar.setProgress(downloadedSize); 
    tv_progress.setText((int) downloadedSize * 100 / max + "%"); 
   } 
   @Override 
   public void downloadEnd() { 
    Log.i("TAG---end", "End"); 
   } 
  }); 
  /** 
   * 下载的点击事件 
   */ 
  downLoad.setOnClickListener(new View.OnClickListener() { 
   @Override 
   public void onClick(View view) { 
    downloadUtil.start(); 
   } 
  }); 
  /** 
   * 暂停的点击事件 
   */ 
  pause.setOnClickListener(new View.OnClickListener() { 
   @Override 
   public void onClick(View view) { 
    downloadUtil.pause(); 
   } 
  }); 
 } 
} 

相关文章

  • Android应用退出登录的实现方法

    Android应用退出登录的实现方法

    每一个app都会有一个”退出登陆”的功能,当点击退出之后需要将所有的Activity都finish掉,开始是想将栈中的所有Activity清除掉,但是没有找到方法,后来用广播实现了。下面小编给大家分享android应用退出登录的实现方法,需要的朋友参考下
    2017-04-04
  • Android嵌套滚动NestedScroll的实现了解一下

    Android嵌套滚动NestedScroll的实现了解一下

    嵌套滚动已经算一个比较常见的特效了,这篇文章主要介绍了Android嵌套滚动NestedScroll的实现了解一下,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-06-06
  • Android实现截屏方式整理(总结)

    Android实现截屏方式整理(总结)

    本篇文章主要介绍了Android 截屏方式整理(总结),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-07-07
  • Android框架学习之Volley和Glide详解

    Android框架学习之Volley和Glide详解

    这篇文章主要给大家介绍了关于Android框架学习之Volley和Glide的相关资料,文中通过示例代码介绍的非常详细,对各位Android开发者们具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-05-05
  • Android开发实现的简单媒体播放器功能示例

    Android开发实现的简单媒体播放器功能示例

    这篇文章主要介绍了Android开发实现的简单媒体播放器功能,结合实例形式分析了Android基于surfaceview实现多媒体视频及音频播放的相关操作技巧,需要的朋友可以参考下
    2017-10-10
  • Android实现图片滚动和页签控件功能的实现代码

    Android实现图片滚动和页签控件功能的实现代码

    这篇文章主要介绍了Android实现图片滚动控件含页签功能的实现代码,具有很好的参考价值,希望对大家有所帮助,一起跟随小编过来看看吧
    2018-05-05
  • Android编程模拟HOME键功能示例

    Android编程模拟HOME键功能示例

    这篇文章主要介绍了Android编程模拟HOME键功能的方法,结合实例形式分析了Android模拟HOME键的原理与相关事件操作技巧,需要的朋友可以参考下
    2016-10-10
  • Android Drawable及其相关类的使用

    Android Drawable及其相关类的使用

    本文主要讲解Android Drawable,这里整理了Drawable 的文档资料和实例代码以及实现效果图,有需要的小伙伴可以参考下
    2016-08-08
  • Android 实现自动打电话与发短信的实例

    Android 实现自动打电话与发短信的实例

    这篇文章主要介绍了Android 实现自动打电话与发短信的实例的相关资料,需要的朋友可以参考下
    2017-05-05
  • Android编程四大组件分别是什么

    Android编程四大组件分别是什么

    大家知道Android编程四大组件分别是什么吗?这篇文章主要为大家详细介绍了Android四大组件,感兴趣的小伙伴们可以参考一下
    2016-06-06

最新评论