鹏达影音
前序:今天开始我会每天更新这个项目的进展和想法,如果有什么不对的地方,也希望网友们提出来,大家一起学习!
注意:我是按照项目的整一个逻辑顺序一点点的去实现,所以篇幅有点长,但只要用心去看和实现,一定会有收获,好了,不瞎比比了,直接进正文。
注意:这边上代码的只有前面搭框架的时候贴上来,其余源码我就不贴上了,直接给你们源码,我就进行源码的分析,然后你们根据
源码好好的去理解,然后打一遍或者多遍就好了。
一.登陆界面
1)效果图(个人很装逼的用了自己的相片去作为登陆页面的imageview)
2)需求分析
(1)界面:因为组件要素都在中间聚集,所以最好用相对布局来写这些组件,因为相对布局可以相对于父容器和控件。最后分析这里有什么组件要素,这里有imageview,textview和progressbar这三个控件;
(2)定时跳转到主界面;
(3)点击登陆界面跳转到主界面;
(4)主界面返回的时候直接跳出app,而不会返回登陆界面或者说多次停留在主页。
3)上代码:
1)布局文件
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffffff" tools:context=".activity.SplashActivity"> <ImageView android:id="@+id/iv_splash_icon" android:layout_width="80dp" android:layout_height="80dp" android:layout_centerInParent="true" android:src="@drawable/ic_launcher" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/iv_splash_icon" android:layout_centerHorizontal="true" android:gravity="center" android:orientation="horizontal"> <ProgressBar android:layout_width="30dp" android:layout_height="30dp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="8dp" android:text="正在启动..." android:textSize="18sp" /> </LinearLayout> </RelativeLayout>
2)SplashActivity
package com.example.pdyingyin.activity; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.MotionEvent; import com.example.pdyingyin.R; //登陆界面 public class SplashActivity extends Activity { private static final String TAG = SplashActivity.class.getSimpleName();//"SplashActivity"
private Handler handler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_splash);
//需求一:定时两秒跳转到主界面 handler.postDelayed(new Runnable() { @Override public void run() { //两秒后才执行到这里 //执行在主线程中(在那个线程里调用这个方法,就在那个线程里执行) startMainActivity(); Log.e(TAG, "当前线程名称==" + Thread.currentThread().getName()); } }, 2000); }
//以上需求一的逻辑代码
private boolean isStartMain = false; /** * 跳转到主页面,并且把当前页面关闭掉 */ private void startMainActivity() { //这个的作用是如果我们之前直接按了键进去首页了,之后在执行主线程的startMainactivity方法时,就不执行逻辑处理了, //从而避免发生多个同种活动存在
//需求三 if(!isStartMain){ isStartMain = true; Intent intent = new Intent(this,MainActivity.class); startActivity(intent); //关闭当前页面 finish(); } }
//需求二:点击界面跳转到主界面 //普通的话,我们会等两秒之后在跳转到我们的主页面,而这个方法的出现就是 //当我们点击界面时,我们就进入到我们的主页面了 @Override public boolean onTouchEvent(MotionEvent event) { Log.e(TAG,"onTouchEvent==Action"+event.getAction()); startMainActivity(); return super.onTouchEvent(event); } @Override protected void onDestroy() { //把所有的消息和回调移除 handler.removeCallbacksAndMessages(null); super.onDestroy(); } }
4)总结:直接看代码里面的注释,按照需求为单位一个一个功能去消化。
二.主界面框架
1)效果图(这边特地用了迷人的小眼睛作为搜索的标志,你懂得,哈哈哈)
(这边只是先把简单的框架给实现下来,之后再慢慢的往里面去加逻辑内容,所以这边大概就是实现了选择器,监听器,标题栏界面这些功能,具体的逻辑功能之后我们再一一讲解。
2)需求分析:
(1)其有四大页面,也就是要用到fragment碎片;
(2)要为每个按钮设立监听;
(3)要为每个按钮设置点击前后变化;
(4)其要有一个特制的标题栏,该标题栏有一个搜索功能,点击搜索的话跳转到搜索的界面,且点击搜索界面其颜色会发生变化,还有两个按钮;
3)实现顺序为:先把碎片简单实现→把标题栏实现→一个一个碎片的内容和标题栏的逻辑功能实现。现在这边我只说碎片实现和标题栏简单实现的,第三步的话放在后面说。
4)上代码:
(1)主界面布局
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <!--标题栏--> <include layout="@layout/titlebar"/> <!--FrameLayout--> <FrameLayout android:id="@+id/fl_main_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" /> <!--RadioGroup--> <RadioGroup android:id="@+id/rg_bottom_tag" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#11000000" android:gravity="center_vertical" android:orientation="horizontal" android:padding="3dp" > <RadioButton android:id="@+id/rb_video" android:text="本地视频" android:drawableTop="@drawable/rb_video_drawable_selector" style="@style/botttom_tag_style" /> <RadioButton android:id="@+id/rb_audio" android:text="本地音频" android:drawableTop="@drawable/rb_audio_drawable_selector" style="@style/botttom_tag_style" /> <RadioButton android:id="@+id/rb_net_video" android:text="网络视频" android:drawableTop="@drawable/rb_net_video_drawable_selector" style="@style/botttom_tag_style" /> <RadioButton android:id="@+id/rb_netaudio" android:text="网络音乐" android:drawableTop="@drawable/rb_netaudio_drawable_selector" style="@style/botttom_tag_style" /> </RadioGroup> </LinearLayout>
(2)标题栏布局
<?xml version="1.0" encoding="utf-8"?> <com.example.testfragment.view.TitleBar xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="55dp" android:background="#ff3097fd" android:gravity="center_vertical" android:orientation="horizontal"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="8dp" android:src="@drawable/toubu3" /> <TextView android:id="@+id/tv_search" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="8dp" android:layout_weight="1" android:background="@drawable/tv_search_bg_selector" android:clickable="true" android:drawableLeft="@drawable/tv_search_drawable_selector" android:drawablePadding="3dp" android:text="全网搜索" android:textColor="@drawable/tv_search_textcolor_selector" android:textSize="14sp" /> <RelativeLayout android:id="@+id/rl_game" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dp"> <TextView android:id="@+id/tv_game" android:layout_width="wrap_content" android:layout_height="wrap_content" android:drawableLeft="@drawable/ic_topbanner_game" /> <ImageView android:layout_width="6dp" android:layout_height="6dp" android:layout_alignRight="@id/tv_game" android:background="@drawable/dot" /> </RelativeLayout> <ImageView android:id="@+id/iv_record" android:layout_marginLeft="5dp" android:layout_marginRight="8dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_topbanner_record" /> </com.example.testfragment.view.TitleBar>
baseview和page
package com.example.testfragment.base; /** * Created by 刘鹏达 on 2017/10/28. */ import android.content.Context; import android.view.View; /** * 作用:基类,公共类, * VideoPager * <p/> * AudioPager * <p/> * NetVideoPager * <p/> * NetAudioPager * 都继承该类 */ public abstract class BasePager { /** * 上下文 */ public final Context context; /** * 接收各个页面的实例 */ public View rootView; public boolean isInitData; public BasePager(Context context) { this.context = context; rootView = initView(); } /** * 强制子页面实现该方法,实现想要的特定的效果 * * @return */ public abstract View initView(); /** * 当子页面,需要绑定数据,或者联网请求数据并且绑定的时候,重写该方法 */ public void initData(){ } }
package com.example.testfragment.page; import android.content.Context; import android.graphics.Color; import android.view.Gravity; import android.view.View; import android.widget.TextView; import com.example.testfragment.base.BasePager; /** * Created by 刘鹏达 on 2017/10/28. */ public class VideoPager extends BasePager { private TextView textView; public VideoPager(Context context){ super(context); } /** * 强制子页面实现该方法,实现想要的特定的效果 * * @return */ @Override //加载控件 public View initView() { textView=new TextView(context); textView.setTextSize(25); textView.setGravity(Gravity.CENTER); textView.setTextColor(Color.RED); return textView; } //加载数据 public void initData(){ super.initData(); textView.setText("本地视频"); } }
其余三个类推。
自定义view代码:
package com.example.testfragment.view; import android.content.Context; import android.content.Intent; import android.util.AttributeSet; import android.view.View; import android.widget.LinearLayout; import android.widget.Toast; import com.example.testfragment.R; /** * 作用:自定义标题栏 */ public class TitleBar extends LinearLayout implements View.OnClickListener { private View tv_search; private View rl_game; private View iv_record; private Context context; /** * 在代码中实例化该类的时候使用这个方法 * @param context */ public TitleBar(Context context) { this(context,null); } /** * 当在布局文件使用该类的时候,Android系统通过这个构造方法实例化该类 * @param context * @param attrs */ public TitleBar(Context context, AttributeSet attrs) { this(context, attrs,0); //LayoutInflater.from(context).inflate(R.layout.titlebar,this); //注意:这边不用写这条语句是因为,我们一般是直接写该组件,然后在这边引用其他布局文件的组件从而变成我们这个 //组件的内部控件,这种方式便于解耦,而我们这边是第二种方式,就直接在自定义控件里面写我们的内部组件,所以 //这边可以忽略这条语句。 } /** * 当需要设置样式的时候,可以使用该方法 * @param context * @param attrs * @param defStyleAttr */ public TitleBar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.context = context; } /** * 当布局文件加载完成的时候回调这个方法 */ @Override protected void onFinishInflate() { super.onFinishInflate(); //得到孩子的实例 tv_search = getChildAt(1); rl_game = getChildAt(2); iv_record = getChildAt(3); //设置点击事件 tv_search.setOnClickListener(this); rl_game.setOnClickListener(this); iv_record.setOnClickListener(this); } @Override public void onClick(View v) { } }
activity文件:
package com.example.testfragment.activity; import android.os.Handler; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.RadioGroup; import android.widget.Toast; import com.example.testfragment.R; import com.example.testfragment.base.BasePager; import com.example.testfragment.fragment.ReplaceFragment; import com.example.testfragment.page.AudioPager; import com.example.testfragment.page.NetAudioPager; import com.example.testfragment.page.NetVideoPager; import com.example.testfragment.page.VideoPager; import java.util.ArrayList; public class MainActivity extends FragmentActivity { private FrameLayout fl_main_content; private RadioGroup rg_bottom_tag; /** * 页面的集合 */ private ArrayList<BasePager> basePagers; /** * 选中的位置 */ private int position; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); fl_main_content=(FrameLayout)findViewById(R.id.fl_main_content); basePagers = new ArrayList<>(); basePagers.add(new VideoPager(this));//添加本地视频页面-0 basePagers.add(new AudioPager(this));//添加本地音乐页面-1 basePagers.add(new NetVideoPager(this));//添加网络视频页面-2 basePagers.add(new NetAudioPager(this));//添加网络音频页面-3 rg_bottom_tag=(RadioGroup)findViewById(R.id.rg_bottom_tag); rg_bottom_tag.setOnCheckedChangeListener(new MyOnCheckedChangeListener()); //设置初始点击按钮 rg_bottom_tag.check(R.id.rb_video); } class MyOnCheckedChangeListener implements RadioGroup.OnCheckedChangeListener { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { switch (checkedId){ default: position = 0; break; case R.id.rb_audio://音频 position = 1; break; case R.id.rb_net_video://网络视频 position = 2; break; case R.id.rb_netaudio://网络音频 position = 3; break; } setFragment(); } } /** * 把页面添加到Fragment中 */ private void setFragment() { //1.得到FragmentManger FragmentManager manager = getSupportFragmentManager(); //2.开启事务 FragmentTransaction ft = manager.beginTransaction(); //3.替换 ft.replace(R.id.fl_main_content,new ReplaceFragment(getBasePager())); //4.提交事务 ft.commit(); } /** * 根据位置得到对应的页面 * @return */ private BasePager getBasePager() { BasePager basePager = basePagers.get(position); if(basePager != null&&!basePager.isInitData){ basePager.initData();//联网请求或者绑定数据 basePager.isInitData = true; } return basePager; } /** * 是否已经退出 */ private boolean isExit = false; @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if(keyCode == KeyEvent.KEYCODE_BACK){ if(position != 0){//不是第一页面 position = 0; rg_bottom_tag.check(R.id.rb_video);//首页 return true; }else if(!isExit){ isExit = true; Toast.makeText(MainActivity.this,"再按一次推出",Toast.LENGTH_SHORT).show(); new Handler().postDelayed(new Runnable() { @Override public void run() { isExit = false; } },2000); return true; } } return super.onKeyDown(keyCode, event); } }
三.完成本地视频的界面,并且完成简单的监听。
1)效果图:
2)需求分析
(1)布局分析:其为listview,listiew的子布局为相对布局,相对布局里面又一个imageview和一个线性布局,textview和直线(view),而一个线性布局里面又有两个textview。
(2)要想有一个自定义的listview的话,必定会有一个子项布局,子项类,适配器,然后在我们的paper中设置适配器并且传入数据。
3)上代码:
(1)子项布局:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:padding="3dp" android:layout_height="100dp"> <ImageView android:id="@+id/iv_icon" android:layout_width="80dp" android:layout_height="80dp" android:layout_marginLeft="8dp" android:src="@drawable/login_icon" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="3dp" android:layout_toRightOf="@id/iv_icon" android:gravity="center_vertical" android:orientation="vertical"> <TextView android:id="@+id/tv_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="视频的名称" android:textColor="@android:color/black" android:textSize="18sp" /> <TextView android:id="@+id/tv_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="5dp" android:text="20:00" android:textColor="#55000000" android:textSize="16sp" /> </LinearLayout> <TextView android:id="@+id/tv_size" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_marginBottom="3dp" android:layout_marginRight="8dp" android:text="20MB" android:textColor="#55000000" android:textSize="16sp" /> <View android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:background="#44000000" android:layout_alignParentBottom="true" android:layout_width="match_parent" android:layout_height="1dp"/> </RelativeLayout>
(2)子项类
package com.example.testfragment.domain; import java.io.Serializable; /** * 作用:代表一个视频和音频 */ public class MediaItem implements Serializable { private String name; private long duration; private long size; private String data; private String artist; private String desc; private String imageUrl; public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public String getImageUrl() { return imageUrl; } public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; } public String getName() { return name; } public void setName(String name) { this.name = name; } public long getDuration() { return duration; } public void setDuration(long duration) { this.duration = duration; } public long getSize() { return size; } public void setSize(long size) { this.size = size; } public String getData() { return data; } public void setData(String data) { this.data = data; } public String getArtist() { return artist; } public void setArtist(String artist) { this.artist = artist; } @Override public String toString() { return "MediaItem{" + "name='" + name + '\'' + ", duration=" + duration + ", size=" + size + ", data='" + data + '\'' + ", artist='" + artist + '\'' + ", desc='" + desc + '\'' + ", imageUrl='" + imageUrl + '\'' + '}'; } }
(3)适配器
package com.example.testfragment.adapter; import android.content.Context; import android.media.browse.MediaBrowser; import android.text.format.Formatter; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import com.example.testfragment.R; import com.example.testfragment.domain.MediaItem; import com.example.testfragment.util.Utils; import java.util.ArrayList; /** * 作用:VideoPager的适配器 */ public class VideoPagerAdapter extends BaseAdapter { private final boolean isVideo; private Context context; private final ArrayList<MediaItem> mediaItems; private Utils utils; public VideoPagerAdapter(Context context, ArrayList<MediaItem> mediaItems, boolean isVideo){ this.context = context; this.mediaItems = mediaItems; this.isVideo = isVideo; utils = new Utils(); } @Override public int getCount() { return mediaItems.size(); } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHoder viewHoder; if(convertView ==null){ convertView = View.inflate(context, R.layout.item_video_pager,null); viewHoder = new ViewHoder(); viewHoder.iv_icon = (ImageView) convertView.findViewById(R.id.iv_icon); viewHoder.tv_name = (TextView) convertView.findViewById(R.id.tv_name); viewHoder.tv_time = (TextView) convertView.findViewById(R.id.tv_time); viewHoder.tv_size = (TextView) convertView.findViewById(R.id.tv_size); convertView.setTag(viewHoder); }else{ viewHoder = (ViewHoder) convertView.getTag(); } //根据position得到列表中对应位置的数据 MediaItem mediaItem = mediaItems.get(position); viewHoder.tv_name.setText(mediaItem.getName()); viewHoder.tv_size.setText(Formatter.formatFileSize(context, mediaItem.getSize())); viewHoder.tv_time.setText(utils.stringForTime((int) mediaItem.getDuration())); if(!isVideo){ //音频 viewHoder.iv_icon.setImageResource(R.drawable.music_default_bg); } return convertView; } static class ViewHoder{ ImageView iv_icon; TextView tv_name; TextView tv_time; TextView tv_size; } }
(4)在videopaper中设置适配器并且加载数据(也就是需要修改上面我们简单实现的videopaper,之后其他三个也要修改)
package com.example.testfragment.page; import android.Manifest; import android.app.Activity; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.database.Cursor; import android.graphics.Color; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.SystemClock; import android.provider.MediaStore; import android.view.Gravity; import android.view.View; import android.widget.AdapterView; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import com.example.testfragment.R; import com.example.testfragment.activity.test; import com.example.testfragment.adapter.VideoPagerAdapter; import com.example.testfragment.base.BasePager; import com.example.testfragment.domain.MediaItem; import com.example.testfragment.util.LogUtil; import java.util.ArrayList; /** * Created by 刘鹏达 on 2017/10/28. */ public class VideoPager extends BasePager { private ListView listview; private TextView tv_nomedia; private ProgressBar pb_loading; private VideoPagerAdapter videoPagerAdapter; /** * 装数据集合 */ private ArrayList<MediaItem> mediaItems; public VideoPager(Context context) { super(context); } private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); if(mediaItems != null && mediaItems.size() >0){ //有数据 //设置适配器 videoPagerAdapter = new VideoPagerAdapter(context,mediaItems,true); listview.setAdapter(videoPagerAdapter); //把文本隐藏 tv_nomedia.setVisibility(View.GONE); }else{ //没有数据 //文本显示 tv_nomedia.setVisibility(View.VISIBLE); } //ProgressBar隐藏 pb_loading.setVisibility(View.GONE); } }; /** * 初始化当前页面的控件,由父类调用 * @return */ @Override public View initView() { View view = View.inflate(context, R.layout.video_pager,null); listview = (ListView) view.findViewById(R.id.listview); tv_nomedia = (TextView) view.findViewById(R.id.tv_nomedia); pb_loading = (ProgressBar) view.findViewById(R.id.pb_loading); //设置ListView的Item的点击事件 listview.setOnItemClickListener(new MyOnItemClickListener()); return view; } //给listview的每个子项设立监听器 class MyOnItemClickListener implements AdapterView.OnItemClickListener { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { MediaItem mediaItem = mediaItems.get(position); //3.传递列表数据-对象-序列化 Intent intent = new Intent(context,test.class); Bundle bundle = new Bundle(); bundle.putSerializable("videolist",mediaItems); intent.putExtras(bundle); intent.putExtra("position",position); context.startActivity(intent); } } @Override public void initData() { super.initData(); LogUtil.e("本地视频的数据被初始化了。。。"); //加载本地视频数据 getDataFromLocal(); } /** * 从本地的sdcard得到数据 * //1.遍历sdcard,后缀名 * //2.从内容提供者里面获取视频 * //3.如果是6.0的系统,动态获取读取sdcard的权限 */ private void getDataFromLocal() { new Thread(){ @Override public void run() { super.run(); isGrantExternalRW((Activity) context); SystemClock.sleep(3000); mediaItems = new ArrayList<>(); ContentResolver resolver = context.getContentResolver(); Uri uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; String[] objs = { MediaStore.Video.Media.DISPLAY_NAME,//视频文件在sdcard的名称 MediaStore.Video.Media.DURATION,//视频总时长 MediaStore.Video.Media.SIZE,//视频的文件大小 MediaStore.Video.Media.DATA,//视频的绝对地址 MediaStore.Video.Media.ARTIST,//歌曲的演唱者 }; Cursor cursor = resolver.query(uri, objs, null, null, null); if(cursor != null){ while (cursor.moveToNext()){ MediaItem mediaItem = new MediaItem(); mediaItems.add(mediaItem);//写在上面 String name = cursor.getString(0);//视频的名称 mediaItem.setName(name); long duration = cursor.getLong(1);//视频的时长 mediaItem.setDuration(duration); long size = cursor.getLong(2);//视频的文件大小 mediaItem.setSize(size); String data = cursor.getString(3);//视频的播放地址 mediaItem.setData(data); String artist = cursor.getString(4);//艺术家 mediaItem.setArtist(artist); } cursor.close(); } //Handler发消息 handler.sendEmptyMessage(10); } }.start(); } /** * 解决安卓6.0以上版本不能读取外部存储权限的问题 * @param activity * @return */ public static boolean isGrantExternalRW(Activity activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && activity.checkSelfPermission( Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { activity.requestPermissions(new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }, 1); return false; } return true; } }
四.点击播放视频的按钮,然后弹出一个播放界面播放我们所选择的资源。
1)效果图:
2)需求分析:
(1)布局分析:使用相对布局,videoview和控制器,还有就是考虑到之后我们加载的时候需要时间还有之后网络视频也要用到这个活动,所以还会又缓冲的布局。途中有组件的隐藏和显示的变换。这边的控制器和videoview都是充满父容器,而videoview先放,控制器后放,达到控制器在videoview的上面的效果。
(2)得到播放视频的名称:根据视频的位置去集合里面得到mediaitem对象,然后根据这个对象获取到数据放在控件中。
(3)电量的显示:写一个广播用来接受系统的电量变化,然后根据接受到的电量的数据来进行图片的修改。注意:这边电量的广播要使用动态注册,因为静态注册的话从开机开始就一直接受这个广播,很耗能量,所以是不允许的。(广播的注册在初始化哪个方法中)。
(4)时间的显示:使用到SimpleDateFormat对象的format(new Data())方法获取时间,然后要想不断的更改时间的话,必定会使用到handler进行动态修改,也就是在handler中会定时1秒就在接受信息的方法中再发送信息,从而到达不断更新的效果。
(5)静音或恢复原来的音量和改变音量:使用到VoiceOnSeekBarChangeListener这个监听类,在监听类onProgressChanged()方法中利用AudioManager的setStreamVolume()方法去改变音量的大小,并且把seekbar的音量进度条设置为当前音量的大小。
(6)视频进度拖动功能:使用VideoOnSeekBarChangeListener监听器类,其中在onProgressChanged()方法中利用videoview.seekTo(progress)方法进行拖动
(7)播放上一个视频:如果视频集合不为空,且大于0的话,position减一,如果此时position还大于=0,我们就设置加载标志可见,获取到该数据对象,设置视频文件名,判断url是否为网络uri,然后就利用videoview对象去根据路径来加载视频,最后设置按钮状态。而如果集合为空且uri不为null,直接设置按钮的状态。
(8)播放下一个视频:同上,只不过position加1.
(9)设置暂停或者播放功能:首先判断该video是否在播放中,如果是,那么该video暂停,把哪个按钮的背景图片换成暂停的图标,如果是暂停的话,那么就播放,把按钮的背景图片缓冲播放的图标。
(10)设置上下视频按钮状态的方法:如果视频只有一个的话,那么全要设成灰色并且不可以点击,如果视频只有两个的话,需要判断其是第一个还是第二个,然后进行特定的处理,如果超过两个视频的话,那么在第一个视频的时候,上为灰色且不可以按,如果是最后一个视频的话,那么就下为灰色且不可以按,其余两个都可以按。最后一种情况的话就是除了上三钟情况,且uri不为空的时候,两个按钮设置为灰色。
(11)设置为全屏或者默认宽高的方法:如果此时是默认的话,那么点击按钮就会调用全屏的方法,只需要将视频的宽和高都设为屏幕的宽,高就可以了,如果是点击默认的宽高的话,需要获取到视频的宽和高,然后获取到屏幕的宽和高,利用以下算法,求出设置视频的宽和高,之后设置,再变换一下背景且标志改一下就好了。
算法:
if (mVideoWidth * height < width * mVideoHeight) { //Log.i("@@@", "image too wide, correcting"); width = height * mVideoWidth / mVideoHeight; } else if (mVideoWidth * height > width * mVideoHeight) { //Log.i("@@@", "image too tall, correcting"); height = width * mVideoHeight / mVideoWidth; }
(12)监听手势从而调用对应的方法:就是搞一个类实现GestureDetector.SimpleOnGestureListener,之后在
onTouchEvent()方法中将事件传递给该手势监听器。
(13)在onTouchEvent()中也设立了光度和音量变化的方法。如果在屏幕的左半边的话就是亮度的变化,且一次增加或者减少20,而光度的变化需要使用到WindowManager.LayoutParams对象,而屏幕的右半边控制音量。
(14)其还使用到onKeyDown()方法控制音量大小。
注意:播放activity得注册,且需要设置一个属性,否则每次横竖屏切换的时候都会重新播放。如下:
android:configChanges="orientation|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"
五.点击本地音频的radiobutton,显示本地音频的界面。
1)效果图:(从这边以下的图都是用真正上线的app的界面,不搞怪了 )
2)需求分析:
(1)布局:很明显,这边的话最外层的布局一定是用了listview组件,而其子项的布局是一个imageview,还有三个textview。
(2)功能实现核心:其向本地的sd卡获取数据,所以还是用到了ContentResolver,其是用来操作其他应用程序的对象,注意,因为我们现在的版本都在6.0以上了,而这边需要读取内部储存信息,其属于危险权限的事情,所以我们需要动态注册,在我们动态注册且用户同意之后,我们利用这个对象获取到数据,而我们获取数据这个过程是属于耗时的,所以我们需要在子线程内去获取这些数据,在结束的时候发送一个空信息给handler,然后接受这个信息,把数据传进我们的适配器中即可。
注意:适配器重点在getview的方法,同时,我们需要记住我们子项有多少个组件,那么我们就会设立一个内部类去获取这些组件。
六.点击任何一个音频,然后跳转到播放音乐的活动中。
1)效果图:
2)需求分析:
(1)布局:
(2)核心功能分析:这边主要利用到的是服务,服务去利用 MediaPlayer对象从后台中去播放音乐,然后利用notification将服务设置为前台服务,这样就方便我们推出之后直接从notification进入我们的播放界面,然后要让活动中知道我们服务的进展,就需要用到Binder,而实现这个机制,我们就需要在服务的内部去实现一个内部类,该内部类将我们要和活动进行交互的方法进行汇总,然后在binder方法中,返回该内部类,然后我们在活动中设置ServiceConnection对象在绑定成功的方法获取该内部类的对象,然后我们就可以在活动中去获取服务中的方法,然后我们就可以在活动中显示后台服务的进展。
七.点击电影预告,跳转到电影预告这个界面
1)效果图:
2)需求分析:
(1)布局:这边也非常明显,就是用了listview,然后其子项的话有两个imageview,两个textview。
(2)核心功能分析:这边的数据我们是要从外界的接口中获取到的,获取到了也要进行解析,而进行解析的话,我们这边用原生的JSONObject和JSONArrays,其余的话统一用框架gson,其使用步骤比如说添加依赖,然后要写你要解析成的对象的属性以及get,set方法,注意,这边特别说明一下,我们要怎么写这些对象和方法,请看下面的例子:
返回的信息是:
根据返回的信息,我们一层一层的去设立属性和对象,比如说,这边的话,第一层有基本类型flag等等,还有一个集合,所以我们就要设置这些属性同时设立这些属性的setget方法,然后如果这些属性对应的值或者说数组里面的任何一个数都没有下拉的标志,那么我们就不用再去创建一个新的对象去设立属性了,如果不是,比如说这边的item集合里面有下拉的标志,那么就是需要我们去专门去构造的,所以这时候,我们又要去构造一个静态内部类,该内部类的名为集合特指的对象名,里面的属性设定的话,我们就拿集合里面的一个例子去设立,比如说这边有id,title,detailur等等,那么我们就要去设立这些属性,设立完这些属性,就又设立setget方法,设置完后又看有什么下拉标志没,如果有继续对该属性进行设立对象,添加属性,方法。
好,当我们解析了这些数据之后,我们就把这些数据放在我们的集合中,然后设置到我们的适配器上,最后我们点击适配器的一个按钮,其就会把该集合和位置传到播放器activity中播放。这边还使用到了刷新和加载更多,所以的话需要自定义三个控件,一个是自定义的刷新,一个为自定义的listview,一个为自定义的加载更多,具体的原理,大家下载了源码,慢慢琢磨,总之,大概的总体思路就上面所说的,不难。
八.点击泛娱乐,跳转到泛娱乐这个界面
1)效果图:
2)需求分析:
(1)布局:其也很明显,跟上面的一样,搞一个listview,然后里面的布局的话,需要分情况去使用子布局,分四种布局,分别为视频,gif图片,图片,文字。
(2)核心功能分析:首先利用GSON去解析数据,而解析数据的话,就需要用到一个对象了,而这个对象怎么设立,就看你的数据了,上面已经进行详细说明了,所以这边就不在说怎么去书写这个对象了,当我们解析完这些数据之后,我们就把这些数据放在一个集合里面去,然后放在我们的适配器中,而我们适配器的getview方法中需要有一个类型的判断,然后加载特定的布局,记载完之后又要根据特定的类型去加载特定的组件,然后加载共有的组件,之后有根据类型去加载特定的数据,最后就加载一些共有的数据了。整体思路就这样。
九.点击全网搜索,进入到搜索界面,然后输入关键字或者语音输入,获取到我们的搜索结果。
1)效果图:
2)需求分析
(1)布局分析:首先进行布局分析,其有一个头部自定义控件,然后下面有一个listview,有加载时要用的progressbar,还有搜索之后显示没有数据的textview。
(2)核心需求分析:其实套路还是一样,就是我们获取输入进去或者是语音输入进去的内容(利用语音输入的核心技术是利用到RecognizerDialog这个对象),然后根据这个内容加上我们定义的接口常量形成一个完整的url,然后利用RequestParams对象利用接口去得到我们要的数据,然后进行解析,解析之后放入到我们的集合里面去,然后将我们的集合配置到我们写好的适配器中,然后给每个适配器项设置监听,点击之后,传入集合和方位的数字到我们的新闻活动中,然后我们就在新闻活动中获取穿过来的数据然后显示,新闻活动很简单就一个textview和webview组成。
好了~~这个项目的框架和核心实现思路已经说完了,这些纯属个人思路,小的实力不济,如果有什么不好的点,欢迎大神们指点,谢谢大家~~~