2.19 Android Diary

  1 AppExplorer 软件管理器实现思路:
  2 
  3 (学习ZhengPing老师的软件管理器的开发教程后的内容整理)
  4 
  5 1、需求:
  6     软件管理器界面全屏,加载过程中有与用户交互的信息,
  7 软件的排列方式有网格方式和列表方式两种,展现出的软件类型有
  8 用户软件和所有软件两种。可对每一个控件实现动画效果。
  9 
 10 2、界面设计:
 11     <1>AppExplorer的整体界面布局(show_app_grid.xml):
 12         最上端显示title,中间部分显示软件,下方两个button分别用来对
 13     软件展示方式和软件类型进行选择。整体使用相对布局的方式。
 14         1)    title部分包括一个ImageView和一个TextView,二者以线性布局
 15         的方式排列。为了美观起见,对LinearLayout进行了paddingLeft设
 16         置。(内边距)
 17         2)    中间部分分列表方式和网格方式来排列软件,在使用其中一种
 18         方式的时候,另一种设置visibility="Gone"即可。默认情况下以网
 19         格方式展示软件,因此先设置ListView的visibility="invisible"
 20         GridView:
 21             android:numColumns="3"  //列数设置为3
 22             android:horizontalSpacing="10px"  //水平列间距
 23             android:verticalSpacing="10px"  //垂直间距
 24             android:listSelector="@drawable/choose_gridview"  //被选定后的背景图片
 25             ...
 26         ListView:
 27             ...
 28             android:listSelector="@drawable/choose_listview"
 29             android:visibility="invisible" //invisible不可见但是占空间,Gone消失后不占地空间        
 30         3)    下方在RelativeLayout中设置两个ImageButton供用户选择,分别
 31         layout_alignParentRight和layout_alignParentLeft        
 32     <2>针对网格方式和列表方式两种效果,设计两个item的布局文件(lv_item.xml、gv_item.xml)
 33         1)gv_item.xml:
 34             因为网格模式下只需要显示软件的icon和软件名,所以用一个LinearLayout
 35         存放一个ImageView和一个TextView即可        
 36         2)lv_item.xml:
 37             列表模式下需要显示软件的icon、软件名、包名,整体用LinearLayout沿水平方向排列,
 38         ImageView作为一项,两个TextView包含在一个LinearLayout中作为另一项
 39     <3>美化:
 40         为了实现全屏显示的效果,在MainActivity的OnCreate方法中添加语句:
 41             getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
 42         去除App自带的title
 43             requestWindowFeature(Window.FEATURE_NO_TITLE);
 44             
 45 3、程序部分:
 46     <1>    设置布局文件
 47             setContentView(R.layout.show_app_grid);
 48         找出布局文件中的两个view
 49             lv = (ListView)this.findViewById(R.id.lv_apps);
 50             gv = (GridView)this.findViewById(R.id.gv_apps);
 51     <2>    为每个view设置一个适配器
 52         class GridViewAdapter extends BaseAdapter{
 53             LayoutInflater inflater; //用来寻找layout下xml布局文件并且将其实例化
 54             List<PackageInfo>  pkgInfos;
 55             public GridViewAdapter(Context context,List<PackageInfo> packageInfos){
 56                 inflater = LayoutInflater.from(context);//将xml文件转换为视图
 57                 this.pkgInfos = packageInfos;
 58                 //复写BaseAdapter的方法
 59                 public int getCount() {
 60                     return pkgInfos.size();//返回list的大小
 61                 ...
 62                 public View getView(int position, View convertView, ViewGroup parent) {
 63                     View view = inflater.inflate(R.layout.gv_item, null);//将布局文件渲染成view        
 64                     //将show_app_grid中的view找出来
 65                     TextView tv =(TextView)view.findViewById(R.id.gv_item_appname);
 66                     ImageView iv = (ImageView)view.findViewById(R.id.gv_item_icon);
 67                     //给view中的控件赋值
 68                     tv.setText(pkgInfos.get(position).applicationInfo.loadLabel(getPackageManager()));
 69                     iv.setImageDrawable(pkgInfos.get(position).applicationInfo.loadIcon(getPackageManager()));
 70                     return view;
 71                 }
 72             }            
 73         }
 74     
 75     <3>    取出软件的packageInfo,区分出用户软件:
 76             packageInfos = getPackageManager().getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_ACTIVITIES);
 77             userPackageInfos = new ArrayList<PackageInfo>();
 78             for(int i=0;i<packageInfos.size();i++) {
 79                 PackageInfo temp = packageInfos.get(i);
 80                 ApplicationInfo appInfo = temp.applicationInfo;
 81                 boolean flag = false;
 82                 //flag为true时表示是用户程序
 83                 if((appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0){ //Updated System app
 84                     flag = true;
 85                 }else if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) //Non-System app
 86                     flag = true;
 87                 if(flag)
 88                     userPackageInfos.add(temp);        
 89         }
 90         
 91     <4>    在主函数中添加ProgressDialog,提高用户体验,使其在等待时看到下列的对话框
 92             pd = ProgressDialog.show(this,"请稍后……","正在搜索你所安装的应用程序",true,false);
 93             Thread t = new Thread(this);
 94             t.start();  
 95         在handler方法中,各个view的适配器设置完毕之后,添加:
 96             pd.dismiss(); //将"正在加载"消息去除掉
 97     
 98     <5>    为了提高效率,将查找安装软件的工作交给线程执行(添加到run方法中),执行完毕后
 99         使用mHandler.sendEmptyMessage(0);向Handler发消息,用于不同线程间的消息传递
100             private Handler mHandler = new Handler(){ // 实现不同线程间消息的传递
101             @Override
102             public void handleMessage(Message msg) {
103                 super.handleMessage(msg);
104                 //当前对象是 mhandler,而这里需要的是activity对象所以强行指定this
105                 showPackageInfos = packageInfos;
106                 //在线程中设置适配器
107                 gv.setAdapter(new GridViewAdapter(MainActivity.this,showPackageInfos)); 
108                 lv.setAdapter(new ListViewAdapter(MainActivity.this,showPackageInfos));
109             }
110     };
111     
112     <6>    找出布局文件中的两个ImageButton
113             ib_change_category = (ImageButton) this.findViewById(R.id.ib_change_category);
114             ib_change_view = (ImageButton)this.findViewById(R.id.ib_change_view);
115         分别设置监听器
116             ib_change_category.setOnClickListener(new OnClickListener() {
117                 @Override
118                 public void onClick(View arg0) {
119                     // TODO Auto-generated method stub
120                     if(allApplication)    {
121                         ib_change_category.setImageResource(R.drawable.user);//改变图片
122                         showPackageInfos = userPackageInfos; //如果展示的是所有程序,点击按钮会切换到用户程序
123                         allApplication = false;
124                         MyToast.MyToastShow(MainActivity.this, R.drawable.user,"用户程序",Toast.LENGTH_SHORT);
125                     }
126                     else  
127                         ...
128                     //重新设置适配器,更新显示的情况
129                     gv.setAdapter(new GridViewAdapter(MainActivity.this, showPackageInfos));
130                     lv.setAdapter(new ListViewAdapter(MainActivity.this, showPackageInfos));
131                 }
132             });
133             ib_change_view.setOnClickListener(new OnClickListener() {        
134                 @Override
135                 public void onClick(View v) {
136                     // TODO Auto-generated method stub
137                     lv.setAdapter(new ListViewAdapter(MainActivity.this, showPackageInfos));
138                     if(gridView)    {  
139                         gridView = false;
140                         gv.setVisibility(View.GONE);//如果显示的是Gridview,点击按钮会让它消失且不占地方
141                         lv.setVisibility(View.VISIBLE);//原本设置为invisible的ListView变得可见了
142                         ib_change_view.setImageResource(R.drawable.list);//改变图片
143                         MyToast.MyToastShow(MainActivity.this, R.drawable.list,"列表显示",Toast.LENGTH_SHORT);
144                     }
145                     else  
146                         ...    
147                 }
148             });        
149         
150     <7>    为ListView和GridView分别设置item点击监听器,在点击view中的item时会触发:
151             gv.setOnItemClickListener(this);
152             lv.setOnItemClickListener(this); 
153         复写监听器的onItemClick函数,设置点击后弹出的AlertDialog,并实现    
154             public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) {
155                 final PackageInfo tempPkgInfo = showPackageInfos.get(position);//由于在类里不能访问局部变量,所以添加final使之能访问
156     
157                 //创建的构造器
158                 AlertDialog.Builder builder = new AlertDialog.Builder(this);
159                 builder.setTitle("选项");//显示在它的title栏上
160                 //第一项是资源文件的ID,第二项是监听器
161                 builder.setItems(R.array.choice,new DialogInterface.OnClickListener() {
162                     @Override
163                     //which 属性用来判断点击的是列表中的第几项,从0开始计数
164                     public void onClick(DialogInterface dialog, int which) {
165                         // TODO Auto-generated method stub
166                         //实现点击每一项内容的具体操作
167                         switch (which) {
168                         case 0:    //打开
169                             String pkgName = tempPkgInfo.packageName;
170                             //一般来说默认启动的activity放在0的位置,但直接传会报空指针异常,所以在packageInfos复制时就做了判断
171                             //This is only filled in if the flag GET_ACTIVITIES was set.
172                             ActivityInfo activityInfo = tempPkgInfo.activities[0];
173                             if(activityInfo==null)    {
174                                 Toast.makeText(MainActivity.this,"没有任何activity", Toast.LENGTH_SHORT);
175                                 return;
176                             }
177                             String activityName = activityInfo.name;
178                             
179                             Intent intent = new Intent();
180                             //使用包名+类名的方式将activity启动起来
181                             intent.setComponent(new ComponentName(pkgName,activityName));
182                             startActivity(intent);                
183                             break;
184                         case 1:    //详细信息
185                             showAppDetail(tempPkgInfo);
186                             break;
187                         case 2: //卸载
188                             Uri pkgUri = Uri.parse("package:"+tempPkgInfo.packageName);
189                             Intent deleteIntent = new Intent();
190                             deleteIntent.setAction(Intent.ACTION_DELETE);
191                             deleteIntent.setData(pkgUri);
192                             startActivityForResult(deleteIntent, 0);//解决了一个bug:startActivity(deleteIntent);
193                             break;
194                         }
195                     }
196                 });
197                 builder.setNegativeButton("取消", null); //默认将AlertDialog取消掉,是AlertDialog的特定属性
198                 builder.create().show();//将菜单显示出来
199             }
200         相关函数            
201             private void showAppDetail(PackageInfo pkgInfo)    {
202                 AlertDialog.Builder builder = new AlertDialog.Builder(this);
203                 builder.setTitle("详细信息");
204                 StringBuffer message =new StringBuffer();
205                 message.append("程序名称 "+pkgInfo.applicationInfo.loadLabel(getPackageManager()));
206                 message.append("\n包名 "+pkgInfo.packageName);
207                 message.append("\n版本号 "+pkgInfo.versionCode);
208                 message.append("\n版本名 "+pkgInfo.versionName);
209                 
210                 builder.setMessage(message.toString());
211                 builder.setIcon(pkgInfo.applicationInfo.loadIcon(getPackageManager()));
212                 builder.setPositiveButton("确定", null);
213                 builder.create().show();
214                 
215             }
216             
217     <8>    增加动画的效果:
218         1)    控制View的动画 
219                 alpha(AlphaAnimation)      渐变透明    
220                 scale(ScaleAnimation)    渐变尺寸伸缩    
221                 translate(TranslateAnimation)   画面转换、位置移动    
222                 rotate(RotateAnimation)   画面转移,旋转动画    
223 
224         2)    控制一个Layout里面子View的动画效果
225                 layoutAnimation(LayoutAnimationController)
226                 gridAnimation(GridLayoutAnimationController)

 

 1 ##    弹出框AlertDialog创建方法:
 2 1、新建builder
 3     AlertDialog.Builder builder = new AlertDialog.Builder(this);
 4 2、设置title、Dialog中的项目
 5     builder.setTitle("详细信息");
 6     //如果有多个项目,就用StringBuffer存放,依次将信息append进去
 7     StringBuffer message =new StringBuffer();
 8     message.append("程序名称 "+pkgInfo.applicationInfo.loadLabel(getPackageManager()));
 9     message.append("\n包名 "+pkgInfo.packageName);
10     message.append("\n版本号 "+pkgInfo.versionCode);
11     //将消息设置到builder中去
12     builder.setMessage(message.toString());
13     builder.setIcon(pkgInfo.applicationInfo.loadIcon(getPackageManager()));
14     builder.setPositiveButton("确定", null);//设置下方按钮,第二个参数是监听器,因为会默认关闭,设为null即可
15     builder.create().show();//将Dialog创建并显示出来
16     
17     
18 ##    为view设置适配器并关联布局文件的方法:
19 1、先在MainActivity中找出view并命名
20 2、为每个view建立一个适配器类,继承某种适配器(BaseAdapter、ArrayAdapter...)
21 复写其方法
22     class GridViewAdapter extends BaseAdapter{
23         LayoutInflater inflater; //用来寻找layout下xml布局文件并且将其实例化
24         List<PackageInfo>  pkgInfos;
25         public GridViewAdapter(Context context,List<PackageInfo> packageInfos){
26             inflater = LayoutInflater.from(context);//将xml文件转换为视图
27             this.pkgInfos = packageInfos;
28             //复写BaseAdapter的方法
29             public int getCount() {
30                 return pkgInfos.size();//返回list的大小
31             }
32             ...
33             public View getView(int position, View convertView, ViewGroup parent) {
34                 View view = inflater.inflate(R.layout.gv_item, null);//将布局文件渲染成view        
35                 //找出view中的控件
36                 TextView tv =(TextView)view.findViewById(R.id.gv_item_appname);
37                 ImageView iv = (ImageView)view.findViewById(R.id.gv_item_icon);
38                 //给view中的控件赋值
39                 tv.setText(pkgInfos.get(position).applicationInfo.loadLabel(getPackageManager()));
40                 iv.setImageDrawable(pkgInfos.get(position).applicationInfo.loadIcon(getPackageManager()));
41                 return view;
42             }
43         }            
44     }
45             

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。