android中apk的下载与自动安装方法介绍

 更新时间:2016年9月20日 19:53  点击:1537
android中apk的下载与自动安装这个虽然不推荐这样来做但有些朋友一定要问我如何实现了,这里整理了一些方法希望对各位有一些帮助了,具体如下。

手机中文件的下载分为后台自动下载和前台下载,我总结了这两种下的实现代码,其中前台下载并实现下载进度条的实现。


第一种:后台下载

/**
 * 后台在下面一个Apk 下载完成后返回下载好的文件
 *
 * @param httpUrl
 * @return
 */
 private File downFile(final String httpUrl) {

 new Thread(new Runnable() {

 @Override
 public void run() {
 try {
 URL url = new URL(httpUrl);
 HttpURLConnection connection = (HttpURLConnection) url.openConnection();
 connection.setRequestMethod("GET");
 connection.setConnectTimeout(5000);
 FileOutputStream fileOutputStream = null;
 InputStream inputStream;
 if (connection.getResponseCode() == 200) {
 inputStream = connection.getInputStream();

 if (inputStream != null) {
 file = getFile(httpUrl);
 fileOutputStream = new FileOutputStream(file);
 byte[] buffer = new byte[1024];
 int length = 0;

 while ((length = inputStream.read(buffer)) != -1) {
 fileOutputStream.write(buffer, 0, length);
 }
 fileOutputStream.close();
 fileOutputStream.flush();
 }
 inputStream.close();
 }
 //下载完成
 //安装
 installApk();
 } catch (MalformedURLException e) {
 e.printStackTrace();
 } catch (IOException e) {
 e.printStackTrace();
 }
 }
 }).start();
 return file;
 }

 /**
 * 根据传过来url创建文件
 *
 */
 private File getFile(String url) {
 File files = new File(Environment.getExternalStorageDirectory().getAbsoluteFile(), getFilePath(url));
 return files;
 }


 /**
 * 截取出url后面的apk的文件名
 *
 * @param url
 * @return
 */
 private String getFilePath(String url) {
 return url.substring(url.lastIndexOf("/"), url.length());
 }

 /**
 * 安装APK
 */
 private void installApk() {
 Intent intent = new Intent(Intent.ACTION_VIEW);
 intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
 startActivity(intent);
 }

 

第二种

//下载apk程序代码
 protected File downLoadFile(String httpUrl) {
                 // TODO Auto-generated method stub
                 final String fileName = "updata.apk";
                 File tmpFile = new File("/sdcard/update");
                 if (!tmpFile.exists()) {
                         tmpFile.mkdir();
                 }
                 final File file = new File("/sdcard/update/" + fileName);
 
                try {
                         URL url = new URL(httpUrl);
                         try {
                                 HttpURLConnection conn = (HttpURLConnection) url
                                                 .openConnection();
                                 InputStream is = conn.getInputStream();
                                 FileOutputStream fos = new FileOutputStream(file);
                                 byte[] buf = new byte[256];
                                 conn.connect();
                                 double count = 0;
                                 if (conn.getResponseCode() >= 400) {
                                         Toast.makeText(Main.this, "连接超时", Toast.LENGTH_SHORT)
                                                         .show();
                                 } else {
                                         while (count <= 100) {
                                                 if (is != null) {
                                                         int numRead = is.read(buf);
                                                         if (numRead <= 0) {
                                                                 break;
                                                         } else {
                                                                 fos.write(buf, 0, numRead);
                                                         }
 
                                                } else {
                                                         break;
                                                 }
 
                                        }
                                 }
 
                                conn.disconnect();
                                 fos.close();
                                 is.close();
                         } catch (IOException e) {
                                 // TODO Auto-generated catch block
 
                                e.printStackTrace();
                         }
                 } catch (MalformedURLException e) {
                         // TODO Auto-generated catch block
 
                        e.printStackTrace();
                 }
 
                return file;
         }
 //打开APK程序代码
 
private void openFile(File file) {
                 // TODO Auto-generated method stub
                 Log.e("OpenFile", file.getName());
                 Intent intent = new Intent();
                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                 intent.setAction(android.content.Intent.ACTION_VIEW);
                 intent.setDataAndType(Uri.fromFile(file),
                                 "application/vnd.android.package-archive");
                 startActivity(intent);
}

上面这种方式直接调用downFile方法,参数传个URL地址就可以了

第三种:

private String url="http://----------------------.apk";
 private File file;
 private ProgressBar pb;
 private TextView tv;
 private int fileSize;
 private int downLoadFileSize;
 private String filename;
 private Handler handler = new Handler() {
 @Override
 public void handleMessage(Message msg) {//定义一个Handler,用于处理下载线程与UI间通讯
 if (!Thread.currentThread().isInterrupted()) {
 switch (msg.what) {
 case 0:
 pb.setMax(fileSize);
 case 1:
 pb.setProgress(downLoadFileSize);
 int result = downLoadFileSize * 100 / fileSize;
 tv.setText(result + "%");
 break;
 case 2:
 finish();
 openFile(file);
 break;
 case -1:
 String error = msg.getData().getString("error");
 Toast.makeText(DownLoadActivity.this, error, Toast.LENGTH_SHORT).show();
 break;
 }
 }
 super.handleMessage(msg);
 }
 };
new Thread() {
 public void run() {
 try {
 down_file(url, "/sdcard/");
 //下载文件,参数:第一个URL,第二个存放路径
 } catch (ClientProtocolException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 } catch (IOException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 }
 }
 }.start();
public void down_file(String url, String path) throws IOException {//下载函数
 filename = url.substring(url.lastIndexOf("/") + 1);//获取文件名
 URL myURL = new URL(url);
 URLConnection conn = myURL.openConnection();
 conn.connect();
 InputStream is = conn.getInputStream();
 this.fileSize = conn.getContentLength();//根据响应获取文件大小
 if (this.fileSize <= 0) throw new RuntimeException("无法获知文件大小 ");
 if (is == null) throw new RuntimeException("stream is null");
 FileOutputStream fos = new FileOutputStream(path + filename);//把数据存入路径+文件名

 byte buf[] = new byte[1024];
 downLoadFileSize = 0;
 sendMsg(0);
 do {
 //循环读取
 int numread = is.read(buf);
 if (numread == -1) {
 break;
 }
 fos.write(buf, 0, numread);
 downLoadFileSize += numread;
 sendMsg(1);//更新进度条
 } while (true);
 file = new File(path + filename);
 sendMsg(2);//通知下载完成
 try {
 is.close();
 } catch (Exception ex) {
 Log.e("tag", "error: " + ex.getMessage(), ex);
 }
 }

 private void sendMsg(int flag) {
 Message msg = new Message();
 msg.what = flag;
 handler.sendMessage(msg);
 }
 //打开APK程序代码

 private void openFile(File file) {
 // TODO Auto-generated method stub
 Log.e("OpenFile", file.getName());
 Intent intent = new Intent();
 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 intent.setAction(android.content.Intent.ACTION_VIEW);
 intent.setDataAndType(Uri.fromFile(file),
 "application/vnd.android.package-archive");
 startActivity(intent);
 }

Dialog是对话框了这个在js中就有一个Dialog命令了,现在在android开发中也有Dialog对话框了,我们下面来看小编整理的一些设置自定义Dialog的位置和大小例子吧,具体的细节如下所示。

在写代码用到对话框的时候,很多时候需要我们自己去搭建对话框的布局,也就是说要自定义dialog,然后在运行出效果的时候,往往对话框大小不成比例,位置也是默认居中的,很不符合我们的需求,下面贴上一部分代码来自定义对话框的位置和大小。

例子1

import android.app.Activity;
import android.app.Dialog;
import android.os.Bundle;
import android.view.Display;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;

public class MainActivity extends Activity {
 TextView tv;
 TextView tv2;
 private Dialog dialog;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 tv= (TextView) findViewById(R.id.tv);
 tv.setOnClickListener(new View.OnClickListener() {
 @Override
 public void onClick(View v) {
 dialog.show();
 }
 });
 //加载对话框
 View view = View.inflate(MainActivity.this,
 R.layout.dialog_register, null);

 dialog = new Dialog(MainActivity.this, R.style.MyDialogStyleBottom);
 dialog.setCanceledOnTouchOutside(false);
 tv2= (TextView) view.findViewById(R.id.tv_yes);
 dialog.setContentView(view);

 tv2.setOnClickListener(new View.OnClickListener() {
 @Override
 public void onClick(View v) {
 dialog.dismiss();
 }
 });

 Window dialogWindow = dialog.getWindow();
 WindowManager.LayoutParams lp = dialogWindow.getAttributes();
 dialogWindow.setGravity(Gravity.CENTER);
 WindowManager m = getWindowManager();
 Display d = m.getDefaultDisplay(); // 获取屏幕宽、高用
 WindowManager.LayoutParams p = dialogWindow.getAttributes(); // 获取对话框当前的参数值
 //p.height = (int) (d.getHeight() * 0.3); // 高度设置为屏幕的0.3
 p.width = (int) (d.getWidth() * 0.85); // 宽度设置为屏幕的0.85
 dialogWindow.setAttributes(p);

 }
}

例子2

自定义对话框(Dialog)位置,大小
package angel.devil;

import android.app.Activity;
import android.app.Dialog;
import android.os.Bundle;
import android.view.Gravity;
import android.view.Window;
import android.view.WindowManager;

public class DialogDemoActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        Dialog dialog = new Dialog(this);
       
        // setContentView可以设置为一个View也可以简单地指定资源ID
        // LayoutInflater
        // li=(LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);
        // View v=li.inflate(R.layout.dialog_layout, null);
        // dialog.setContentView(v);
        dialog.setContentView(R.layout.dialog_layout);

        dialog.setTitle("Custom Dialog");

        /*
         * 获取圣诞框的窗口对象及参数对象以修改对话框的布局设置,
         * 可以直接调用getWindow(),表示获得这个Activity的Window
         * 对象,这样这可以以同样的方式改变这个Activity的属性.
         */
        Window dialogWindow = dialog.getWindow();
        WindowManager.LayoutParams lp = dialogWindow.getAttributes();
        dialogWindow.setGravity(Gravity.LEFT | Gravity.TOP);

        /*
         * lp.x与lp.y表示相对于原始位置的偏移.
         * 当参数值包含Gravity.LEFT时,对话框出现在左边,所以lp.x就表示相对左边的偏移,负值忽略.
         * 当参数值包含Gravity.RIGHT时,对话框出现在右边,所以lp.x就表示相对右边的偏移,负值忽略.
         * 当参数值包含Gravity.TOP时,对话框出现在上边,所以lp.y就表示相对上边的偏移,负值忽略.
         * 当参数值包含Gravity.BOTTOM时,对话框出现在下边,所以lp.y就表示相对下边的偏移,负值忽略.
         * 当参数值包含Gravity.CENTER_HORIZONTAL时
         * ,对话框水平居中,所以lp.x就表示在水平居中的位置移动lp.x像素,正值向右移动,负值向左移动.
         * 当参数值包含Gravity.CENTER_VERTICAL时
         * ,对话框垂直居中,所以lp.y就表示在垂直居中的位置移动lp.y像素,正值向右移动,负值向左移动.
         * gravity的默认值为Gravity.CENTER,即Gravity.CENTER_HORIZONTAL |
         * Gravity.CENTER_VERTICAL.
         *
         * 本来setGravity的参数值为Gravity.LEFT | Gravity.TOP时对话框应出现在程序的左上角,但在
         * 我手机上测试时发现距左边与上边都有一小段距离,而且垂直坐标把程序标题栏也计算在内了,
         * Gravity.LEFT, Gravity.TOP, Gravity.BOTTOM与Gravity.RIGHT都是如此,据边界有一小段距离
         */
        lp.x = 100; // 新位置X坐标
        lp.y = 100; // 新位置Y坐标
        lp.width = 300; // 宽度
        lp.height = 300; // 高度
        lp.alpha = 0.7f; // 透明度

        // 当Window的Attributes改变时系统会调用此函数,可以直接调用以应用上面对窗口参数的更改,也可以用setAttributes
        // dialog.onWindowAttributesChanged(lp);
        dialogWindow.setAttributes(lp);

        /*
         * 将对话框的大小按屏幕大小的百分比设置
         */
//        WindowManager m = getWindowManager();
//        Display d = m.getDefaultDisplay(); // 获取屏幕宽、高用
//        WindowManager.LayoutParams p = dialogWindow.getAttributes(); // 获取对话框当前的参数值
//        p.height = (int) (d.getHeight() * 0.6); // 高度设置为屏幕的0.6
//        p.width = (int) (d.getWidth() * 0.65); // 宽度设置为屏幕的0.65
//        dialogWindow.setAttributes(p);

        dialog.show();

    }
}

 

 

布局文件:

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:background="#00FF00"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />

</LinearLayout>

 

dialog_layout.xml


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout_root"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal"
    android:padding="10dp" >

    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="10dp"
        android:src="@drawable/ic_launcher" />

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="A Dialog"
        android:textColor="#FFF" />

</LinearLayout>

Eclipse编辑器是许多开发者会用到的一个编辑器了,我信下面来为各位介绍一篇关于Eclipse编辑器的安装操作步骤,具体如下所示。
重要,更新安卓SDK的时候要有梯子,需要滋备hosts文件
 
 
 
在windows安装Android的开发环境不简单也说不上算复杂,本文写给第一次想在自己Windows上建立Android开发环境投入Android浪潮的朋友们,为了确保大家能顺利完成开发环境的搭建,文章写的尽量详细,希望对准备进入Android开发的朋友有帮助。
 
本教程将分为五个步骤来完成Android开发环境的部署。
第一步:安装JDK。
第二步:配置Windows上JDK的变量环境 。
第三步: 下载安装Eclipse 。
第四步:下载安装Android SDK 。
第五步:为Eclipse安装ADT插件。

第一步:安装JDK

要下载Oracle公司的JDK可以百度“JDK”进入Oracle公司的JDK下载页面(当前下载页面地址为http://www.oracle.com/technetwork/java/javase/downloads/index.html),选择自己电脑系统的对应版本即可。
 
下载到本地电脑后双击进行安装。JDK默认安装成功后,会在系统目录下出现两个文件夹,一个代表jdk,一个代表jre。
 
JDK的全称是Java SE Development Kit,也就是Java 开发工具箱。SE表示标准版。JDK是Java的核心,包含了Java的运行环境(Java Runtime Environment),一堆Java工具和给开发者开发应用程序时调用的Java类库。
 
我们可以打开jdk的安装目录下的Bin目录,里面有许多后缀名为exe的可执行程序,这些都是JDK包含的工具。通过第二步讲到的配置JDK的变量环境,我们可以方便地调用这些工具及它们的命令。
 
 
JDK包含的基本工具主要有:
javac:Java编译器,将源代码转成字节码。
jar:打包工具,将相关的类文件打包成一个文件。
javadoc:文档生成器,从源码注释中提取文档。
jdb:debugger,调试查错工具。
java:运行编译后的java程序。
 
第二步:配置Windows上JDK的变量环境
 
很多刚学java开发的人按照网上的教程可以很轻松配置好Windows上JDK的变量环境,但是为什么要这么配置并没有多想。
 
我们平时打开一个应用程序,一般是通过桌面的应用程序图标双击或单击系统开始菜单中应用程序的菜单链接,无论是桌面的快捷图标还是菜单链接都包含了应用程序的安装位置信息,打开它们的时候系统会按照这些位置信息找到安装目录然后启动程序。
 
 
知道了一个应用程序的安装目录位置,我们也可以通过命令行工具打开,如QQ的位置为:C:\Program Files (x86)\Tencent\QQ\QQProtect\Bin,QQ的应用程序名为为QQProtect.exe,那么我们打开命令行工具,然后进入到“C:\Program Files (x86)\Tencent\QQ\QQProtect\Bin”目录,再输入“QQProtect”,即可运行qq。
 
 
如果我们希望打开命令行工具后,直接输入“QQProtect”就能启动qq程序,而不是每次都进入qq的安装目录再启动,这个时候可以通过配置系统环境变量Path来实现。右击“我的电脑”,选择“属性”,在打开窗口中点击左边的“高级系统设置”,出现“系统属性”窗口,在“高级”选项卡下面点击“环境变量”。
 
 
编辑系统变量名“Path”,在“Path”变量(字符串内容)的后面追加qq的安装目录:;C:\Program Files (x86)\Tencent\QQ\QQProtect\Bin 注意追加的时候要在目录字符串的前面加个英文的分号;,英文分号是用来区分Path里面不同的路径。
 
 
确定保存后,再回到命令窗口,不管在任何目录下,你只要输入qqprotect的命令,qq就会启动。
 
 
通过启动qq的例子,我们总结下:当要求系统启动一个应用程序时,系统会先在当前目录下查找,如果没有则在系统变量Path指定的路径去查找。前面我们说了JDK包含了一堆开发工具,这些开发工具都在JDK的安装目录下,为了方便使用这些开发工具,我们有必要把JDK的安装目录设置了系统变量。这就是为什么在Windows安装了JDK后需要设置JDK的bin目录为系统环境变量的原因。
 
为了配置JDK的系统变量环境,我们需要设置三个系统变量,分别是JAVA_HOME,Path和CLASSPATH。下面是这三个变量的设置防范。
 
JAVA_HOME
先设置这个系统变量名称,变量值为JDK在你电脑上的安装路径:C:\Program Files\Java\jdk1.8.0_20。创建好后则可以利用%JAVA_HOME%作为JDK安装目录的统一引用路径。
 
Path
PATH属性已存在,可直接编辑,在原来变量后追加:;%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin 。
 
CLASSPATH 
设置系统变量名为:CLASSPATH  变量值为:.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar 。
注意变量值字符串前面有一个"."表示当前目录,设置CLASSPATH 的目的,在于告诉Java执行环境,在哪些目录下可以找到您所要执行的Java程序所需要的类或者包。
 
第三步: 下载安装Eclipse
 
Eclipse为Java应用程序及Android开发的IDE(集成开发环境)。Eclipse不需要安装,下载后把解压包解压后,剪切eclipse文件夹到你想安装的地方,打开时设置你的工作目录即可。
 
Eclipse的版本有多个,这里选择下载Eclipse IDE for Java EE Developers这个版本。
 
 
 
第四步:下载安装Android SDK
 
配置了JDK变量环境,安装好了Eclipse,这个时候如果只是开发普通的JAVA应用程序,那么Java的开发环境已经准备好了。我们要通过Eclipse来开发Android应用程序,那么我们需要下载Android SDK(Software Development Kit)和在Eclipse安装ADT插件,这个插件能让Eclipse和Android SDK关联起来。
 
Android SDK提供了开发Android应用程序所需的API库和构建、测试和调试Android应用程序所需的开发工具。
打开http://developer.android.com/sdk/index.html,我们发现google提供了集成了Eclipse的Android Developer Tools,因为我们这次是已经下载了Eclipse,所以我们选择单独下载Android SDK。
 
 
下载后双击安装,指定Android SDK的安装目录,为了方便使用Android SDK包含的开发工具,我们在系统环境变量中的Path设置Android SDK的安装目录下的tools目录。
 
在Android SDK的安装目录下,双击“SDK Manager.exe”,打开Android SDK Manager,Android SDK Manage负责下载或更新不同版本的SDK包,我们看到默认安装的Android SDK Manager只安装了一个版本的sdk tools。
 
 
打开Android SDK Manager,它会获取可安装的sdk版本,但是国内有墙,有时候会出现获取失败的情况。
 
 
从弹出的log窗口中,我们可以看到连接 “https://dl-ssl.google.com”失败了。我们通过ping命令,发现果然网络不通。
 
 
从万能的互联网上,我们找到了解决这个问题的方案,而且行之有效。
 
更改host文件
首先更改host文件,host文件在C:\Windows\System32\drivers\etc目录下,用记事本打开“hosts”文件,将下面两行信息追加到hosts文件末尾,保存即可。如果你的是windows8系统可能没有权限修改host文件,可以右击hosts文件,将Users组设置为可对hosts文件完全控制的权限即可。
 
203.208.46.146 dl.google.com
203.208.46.146 dl-ssl.google.com
 
上面两行放在host文件的意思是将本地访问dl.google.com和dl-ssl.google.com定向到ip地址为203.208.46.146的服务器上。
 
 
将Android SDK Manage上的https请求改成http请求
打开Android SDK Manager,在Tools下的 Options 里面,有一项 Force https://..sources to be fetched using http://... 将这一项勾选上,就可以了。
 
 
再打开Android SDK Manager.exe,正常情况下就可以下载Android的各个版本的sdk了。你只需要选择想要安装或更新的安装包安装即可。这里是比较耗时的过程,还会出现下载失败的情况,失败的安装包只需要重新选择后再安装就可以了。
 
 
如果通过更改DNS也无法下载Android SDK,还有两个方法,第一个是自备梯子FQ,第二个是从这个网站上下载,下载的地址是:http://www.androiddevtools.cn/
 
第五步:为Eclipse安装ADT插件
 
前面我们已经配置好了java的开发环境,安装了开发Android的IDE,下载安装了Android SDK,但是Eclipse还没有和Android SDK进行关联,也就是它们现在是互相独立的,就好比枪和子弹分开了。为了使得Android应用的创建,运行和调试更加方便快捷,Android的开发团队专门针对Eclipse IDE定制了一个插件:Android Development Tools(ADT)。
下面是在线安装ADT的方法:
启动Eclipse,点击 Help菜单 -> Install New Software… ?,点击弹出对话框中的Add… 按钮。
 
然后在弹出的对话框中的Location中输入:http://dl-ssl.google.com/android/eclipse/,Name可以输入ADT,点击“OK”按钮。
 
 
在弹出的对话框选择要安装的工具,然后下一步就可以了。
 
 
安装好后会要求你重启Eclipse,Eclipse会根据目录的位置智能地和它相同目录下Android sdk进行关联,如果你还没有通过sdk manager工具安装Android任何版本的的sdk,它会提醒立刻安装它们。
 
 
如果Eclipse没有自动关联Android sdk的安装目录,那么你可以在打开的Eclipse选择 Window -> Preferences ,在弹出面板中就会看到Android设置项,填上安装的SDK路径,则会出现刚才在SDK中安装的各平台包,按OK完成配置。
 
 
到这里,我们在windows上的Android上的开发环境搭建就完成了,这时候,你用Eclipse的File——》New——》Project...新建一个项目的时候,就会看到建立Android项目的选项了。
 
android 用到一个HorizontalListView 可以实现横向加载数据了这里我们就一起来看一个HorizontalListView横向动态加载数据例子,具体如下所示。

这个自定义的控件可以实现横向的动态数据加载,话不多说,下面上代码。(使用方法和普通listview一致)

1、在布局里用HorizontalScrollView包含一个ListView;
2、利用GridView,把它的行数设为1行;

HorizontalListView.java:
package cn.zmit.xianneng.widget;

import java.util.LinkedList;
import java.util.Queue;

import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.Scroller;

public class HorizontalListView extends AdapterView<ListAdapter> {

public boolean mAlwaysOverrideTouch = true;
protected ListAdapter mAdapter;
private int mLeftViewIndex = -1;
private int mRightViewIndex = 0;
protected int mCurrentX;
protected int mNextX;
private int mMaxX = Integer.MAX_VALUE;
private int mDisplayOffset = 0;
protected Scroller mScroller;
private GestureDetector mGesture;
private Queue<View> mRemovedViewQueue = new LinkedList<View>();
private OnItemSelectedListener mOnItemSelected;
private OnItemClickListener mOnItemClicked;
private OnItemLongClickListener mOnItemLongClicked;
private boolean mDataChanged = false;
public HorizontalListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}

private synchronized void initView() {
mLeftViewIndex = -1;
mRightViewIndex = 0;
mDisplayOffset = 0;
mCurrentX = 0;
mNextX = 0;
mMaxX = Integer.MAX_VALUE;
mScroller = new Scroller(getContext());
mGesture = new GestureDetector(getContext(), mOnGesture);
}

@Override
public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) {
mOnItemSelected = listener;
}

@Override
public void setOnItemClickListener(AdapterView.OnItemClickListener listener){
mOnItemClicked = listener;
}

@Override
public void setOnItemLongClickListener(AdapterView.OnItemLongClickListener listener) {
mOnItemLongClicked = listener;
}

private DataSetObserver mDataObserver = new DataSetObserver() {

@Override
public void onChanged() {
synchronized(HorizontalListView.this){
mDataChanged = true;
}
invalidate();
requestLayout();
}

@Override
public void onInvalidated() {
reset();
invalidate();
requestLayout();
}

};

@Override
public ListAdapter getAdapter() {
return mAdapter;
}

@Override
public View getSelectedView() {
//TODO: implement
return null;
}

@Override
public void setAdapter(ListAdapter adapter) {
if(mAdapter != null) {
mAdapter.unregisterDataSetObserver(mDataObserver);
}
mAdapter = adapter;
mAdapter.registerDataSetObserver(mDataObserver);
reset();
}

private synchronized void reset(){
initView();
removeAllViewsInLayout();
requestLayout();
}

@Override
public void setSelection(int position) {
//TODO: implement
}

private void addAndMeasureChild(final View child, int viewPos) {
LayoutParams params = child.getLayoutParams();
if(params == null) {
params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
}

addViewInLayout(child, viewPos, params, true);
child.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));
}

@Override
protected synchronized void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);

if(mAdapter == null){
return;
}

if(mDataChanged){
int oldCurrentX = mCurrentX;
initView();
removeAllViewsInLayout();
mNextX = oldCurrentX;
mDataChanged = false;
}

if(mScroller.computeScrollOffset()){
int scrollx = mScroller.getCurrX();
mNextX = scrollx;
}

if(mNextX <= 0){
mNextX = 0;
mScroller.forceFinished(true);
}
if(mNextX >= mMaxX) {
mNextX = mMaxX;
mScroller.forceFinished(true);
}

int dx = mCurrentX – mNextX;

removeNonVisibleItems(dx);
fillList(dx);
positionItems(dx);

mCurrentX = mNextX;

if(!mScroller.isFinished()){
post(new Runnable(){
@Override
public void run() {
requestLayout();
}
});

}
}

private void fillList(final int dx) {
int edge = 0;
View child = getChildAt(getChildCount()-1);
if(child != null) {
edge = child.getRight();
}
fillListRight(edge, dx);

edge = 0;
child = getChildAt(0);
if(child != null) {
edge = child.getLeft();
}
fillListLeft(edge, dx);
}

private void fillListRight(int rightEdge, final int dx) {
while(rightEdge + dx < getWidth() && mRightViewIndex < mAdapter.getCount()) {

View child = mAdapter.getView(mRightViewIndex, mRemovedViewQueue.poll(), this);
addAndMeasureChild(child, -1);
rightEdge += child.getMeasuredWidth();

if(mRightViewIndex == mAdapter.getCount()-1) {
mMaxX = mCurrentX + rightEdge – getWidth();
}

if (mMaxX < 0) {
mMaxX = 0;
}
mRightViewIndex++;
}

}

private void fillListLeft(int leftEdge, final int dx) {
while(leftEdge + dx > 0 && mLeftViewIndex >= 0) {
View child = mAdapter.getView(mLeftViewIndex, mRemovedViewQueue.poll(), this);
addAndMeasureChild(child, 0);
leftEdge -= child.getMeasuredWidth();
mLeftViewIndex–;
mDisplayOffset -= child.getMeasuredWidth();
}
}

private void removeNonVisibleItems(final int dx) {
View child = getChildAt(0);
while(child != null && child.getRight() + dx <= 0) {
mDisplayOffset += child.getMeasuredWidth();
mRemovedViewQueue.offer(child);
removeViewInLayout(child);
mLeftViewIndex++;
child = getChildAt(0);

}

child = getChildAt(getChildCount()-1);
while(child != null && child.getLeft() + dx >= getWidth()) {
mRemovedViewQueue.offer(child);
removeViewInLayout(child);
mRightViewIndex–;
child = getChildAt(getChildCount()-1);
}
}

private void positionItems(final int dx) {
if(getChildCount() > 0){
mDisplayOffset += dx;
int left = mDisplayOffset;
for(int i=0;i<getChildCount();i++){
View child = getChildAt(i);
int childWidth = child.getMeasuredWidth();
child.layout(left, 0, left + childWidth, child.getMeasuredHeight());
left += childWidth + child.getPaddingRight();
}
}
}

public synchronized void scrollTo(int x) {
mScroller.startScroll(mNextX, 0, x – mNextX, 0);
requestLayout();
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean handled = super.dispatchTouchEvent(ev);
handled |= mGesture.onTouchEvent(ev);
return handled;
}

protected boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
synchronized(HorizontalListView.this){
mScroller.fling(mNextX, 0, (int)-velocityX, 0, 0, mMaxX, 0, 0);
}
requestLayout();

return true;
}

protected boolean onDown(MotionEvent e) {
mScroller.forceFinished(true);
return true;
}

private OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() {

@Override
public boolean onDown(MotionEvent e) {
return HorizontalListView.this.onDown(e);
}

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
return HorizontalListView.this.onFling(e1, e2, velocityX, velocityY);
}

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {

synchronized(HorizontalListView.this){
mNextX += (int)distanceX;
}
requestLayout();

return true;
}

@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
for(int i=0;i<getChildCount();i++){
View child = getChildAt(i);
if (isEventWithinView(e, child)) {
if(mOnItemClicked != null){
mOnItemClicked.onItemClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ));
}
if(mOnItemSelected != null){
mOnItemSelected.onItemSelected(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ));
}
break;
}

}
return true;
}

@Override
public void onLongPress(MotionEvent e) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (isEventWithinView(e, child)) {
if (mOnItemLongClicked != null) {
mOnItemLongClicked.onItemLongClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i));
}
break;
}

}
}

private boolean isEventWithinView(MotionEvent e, View child) {
Rect viewRect = new Rect();
int[] childPosition = new int[2];
child.getLocationOnScreen(childPosition);
int left = childPosition[0];
int right = left + child.getWidth();
int top = childPosition[1];
int bottom = top + child.getHeight();
viewRect.set(left, top, right, bottom);
return viewRect.contains((int) e.getRawX(), (int) e.getRawY());
}
};

}

[!--infotagslink--]

相关文章

  • php 中file_get_contents超时问题的解决方法

    file_get_contents超时我知道最多的原因就是你机器访问远程机器过慢,导致php脚本超时了,但也有其它很多原因,下面我来总结file_get_contents超时问题的解决方法总结。...2016-11-25
  • HTTP 408错误是什么 HTTP 408错误解决方法

    相信很多站长都遇到过这样一个问题,访问页面时出现408错误,下面一聚教程网将为大家介绍408错误出现的原因以及408错误的解决办法。 HTTP 408错误出现原因: HTT...2017-01-22
  • php抓取网站图片并保存的实现方法

    php如何实现抓取网页图片,相较于手动的粘贴复制,使用小程序要方便快捷多了,喜欢编程的人总会喜欢制作一些简单有用的小软件,最近就参考了网上一个php抓取图片代码,封装了一个php远程抓取图片的类,测试了一下,效果还不错分享...2015-10-30
  • Android子控件超出父控件的范围显示出来方法

    下面我们来看一篇关于Android子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。 <RelativeLayout xmlns:an...2016-10-02
  • ps把文字背景变透明的操作方法

    ps软件是现在非常受大家喜欢的一款软件,有着非常不错的使用功能。这次文章就给大家介绍下ps把文字背景变透明的操作方法,喜欢的一起来看看。 1、使用Photoshop软件...2017-07-06
  • C#实现HTTP下载文件的方法

    这篇文章主要介绍了C#实现HTTP下载文件的方法,包括了HTTP通信的创建、本地文件的写入等,非常具有实用价值,需要的朋友可以参考下...2020-06-25
  • intellij idea快速查看当前类中的所有方法(推荐)

    这篇文章主要介绍了intellij idea快速查看当前类中的所有方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-09-02
  • Mysql select语句设置默认值的方法

    1.在没有设置默认值的情况下: 复制代码 代码如下:SELECT userinfo.id, user_name, role, adm_regionid, region_name , create_timeFROM userinfoLEFT JOIN region ON userinfo.adm_regionid = region.id 结果:...2014-05-31
  • js导出table数据到excel即导出为EXCEL文档的方法

    复制代码 代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta ht...2013-10-13
  • mysql 批量更新与批量更新多条记录的不同值实现方法

    批量更新mysql更新语句很简单,更新一条数据的某个字段,一般这样写:复制代码 代码如下:UPDATE mytable SET myfield = 'value' WHERE other_field = 'other_value';如果更新同一字段为同一个值,mysql也很简单,修改下where即...2013-10-04
  • js基础知识(公有方法、私有方法、特权方法)

    本文涉及的主题虽然很基础,在许多人看来属于小伎俩,但在JavaScript基础知识中属于一个综合性的话题。这里会涉及到对象属性的封装、原型、构造函数、闭包以及立即执行表达式等知识。公有方法 公有方法就是能被外部访问...2015-11-08
  • ps怎么制作倒影 ps设计倒影的方法

    ps软件是一款非常不错的图片处理软件,有着非常不错的使用效果。这次文章要给大家介绍的是ps怎么制作倒影,一起来看看设计倒影的方法。 用ps怎么做倒影最终效果&#819...2017-07-06
  • PHP 验证码不显示只有一个小红叉的解决方法

    最近想自学PHP ,做了个验证码,但不知道怎么搞的,总出现一个如下图的小红叉,但验证码就是显示不出来,原因如下 未修改之前,出现如下错误; (1)修改步骤如下,原因如下,原因是apache权限没开, (2)点击打开php.int., 搜索extension=ph...2013-10-04
  • Android开发中findViewById()函数用法与简化

    findViewById方法在android开发中是获取页面控件的值了,有没有发现我们一个页面控件多了会反复研究写findViewById呢,下面我们一起来看它的简化方法。 Android中Fin...2016-09-20
  • c#中分割字符串的几种方法

    单个字符分割 string s="abcdeabcdeabcde"; string[] sArray=s.Split('c'); foreach(string i in sArray) Console.WriteLine(i.ToString()); 输出下面的结果: ab de...2020-06-25
  • Android模拟器上模拟来电和短信配置

    如果我们的项目需要做来电及短信的功能,那么我们就得在Android模拟器开发这些功能,本来就来告诉我们如何在Android模拟器上模拟来电及来短信的功能。 在Android模拟...2016-09-20
  • 安卓手机wifi打不开修复教程,安卓手机wifi打不开解决方法

    手机wifi打不开?让小编来告诉你如何解决。还不知道的朋友快来看看。 手机wifi是现在生活中最常用的手机功能,但是遇到手机wifi打不开的情况该怎么办呢?如果手机wifi...2016-12-21
  • js控制页面控件隐藏显示的两种方法介绍

    javascript控制页面控件隐藏显示的两种方法,方法的不同之处在于控件隐藏后是否还在页面上占位 方法一: 复制代码 代码如下: document.all["panelsms"].style.visibility="hidden"; document.all["panelsms"].style.visi...2013-10-13
  • 连接MySql速度慢的解决方法(skip-name-resolve)

    最近在Linux服务器上安装MySql5后,本地使用客户端连MySql速度超慢,本地程序连接也超慢。 解决方法:在配置文件my.cnf的[mysqld]下加入skip-name-resolve。原因是默认安装的MySql开启了DNS的反向解析。如果禁用的话就不能...2015-10-21
  • 夜神android模拟器设置代理的方法

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