前面讲了下面的菜单布局与中间的fragmentLayout的布局与实现,今天就写了顶部title的布局以及去搜索本地sd卡里的视频。
一、顶部布局实现
效果图如下:
1、分析下:
a、这个顶部布局是个LinearLayout布局
b、左侧这个321影音是个ImageView,然后这个搜索框是一个TextView,右侧的游戏图标是个相对布局,里面是一个textview和一个Image构成的点,最右侧的那个记录是一个Imageview
c、最主要的是中间的这个搜索框,为什么是一个textview呢,这个组件的drawableleft是一个搜索图标,背景是一个矩形,然后这个textview还可以点击,就实现了这个搜索框的设计,详细见代码。
2、activity_main.xml里,把顶部的这个布局作为一个外部布局文件,然后引入到这里。
<?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/activity_titlebar"/> <!--frameLayout --> <FrameLayout android:background="#22000000" android:id="@+id/fl_main_content" android:layout_weight="1" android:layout_width="match_parent" android:layout_height="0dp"/> . . . .
2、activity_titlebar.xml里,最外层是一个LinearLayout,这里显示的是一个TitleBar,是因为我自定义了一个类,继承了LinearLayout,这个类实现了标题栏布局里组件的初始化、点击事件。
<?xml version="1.0" encoding="utf-8"?> <com.yuanlp.mobileplayer.view.TitleBar xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:background="#ff3097fd" android:gravity="center_vertical" android:layout_height="55dp"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_topbanner_logo" android:layout_marginLeft="8dp" /> <TextView android:layout_weight="1" android:layout_marginLeft="5dp" android:drawablePadding="3dp" android:id="@+id/tv_search" android:drawableLeft="@drawable/tv_search_drawable_selector" android:background="@drawable/tv_search_bg_selector" android:textColor="@drawable/ic_tv_search_textcolor_selector" android:clickable="true" android:textSize="14sp" android:text="全网搜索" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <RelativeLayout android:id="@+id/rl_game" android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:id="@+id/tv_game" android:drawableLeft="@drawable/ic_topbanner_game" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <ImageView android:layout_alignRight="@+id/tv_game" android:background="@drawable/dot" android:layout_width="6dp" android:layout_height="6dp"/> </RelativeLayout> <ImageView android:id="@+id/iv_record" android:layout_marginLeft="5dp" android:layout_marginRight="8dp" android:background="@drawable/ic_topbanner_record" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </com.yuanlp.mobileplayer.view.TitleBar>
3、TitleBar 用于对title布局里组件的实例化与点击事件
package com.yuanlp.mobileplayer.view; import android.content.Context; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.view.View; import android.widget.LinearLayout; import android.widget.Toast; import com.yuanlp.mobileplayer.R; /** * Created by 原立鹏 on 2017/7/14. * 自定义标题栏类 */ public class TitleBar extends LinearLayout implements View.OnClickListener { private View tv_search; //输入框的ID对应的控件 private View rl_ganme; 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, @Nullable AttributeSet attrs) { this(context, attrs,0); } /** * 当需要设置样式的时候,可以使用该方法 * @param context * @param attrs * @param defStyleAttr */ public TitleBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.context=context; } /** * 当布局文件加载完成后,回调这个方法 */ @Override protected void onFinishInflate() { super.onFinishInflate(); //得到子控件实例 tv_search=getChildAt(1); rl_ganme=getChildAt(2); iv_record=getChildAt(3); //设置点击事件 tv_search.setOnClickListener(this); rl_ganme.setOnClickListener(this); iv_record.setOnClickListener(this); } /** * Called when a view has been clicked. * * @param v The view that was clicked. */ @Override public void onClick(View v) { switch (v.getId()){ case R.id.tv_search: Toast.makeText(context,"点击了搜索",Toast.LENGTH_SHORT).show(); break; case R.id.rl_game: Toast.makeText(context,"点击了游戏",Toast.LENGTH_SHORT).show(); break; case R.id.iv_record: Toast.makeText(context,"点击了播放历史",Toast.LENGTH_SHORT).show(); break; } } }
至此,整个主界面的布局完成,后面的就是完善细节。
二、搜索本地视频
搜索本地视频,有2种办法:
第一种是笨方法,检索SD卡里后缀名,获取视频,
第二种:Android系统会在SD卡插拔后,自动通过Media provder来检索视频,然后存储,然后通过ContentProvder来对外公布这些视频信息。
在这里主要使用第二种办法。
1、定义展现视频列表的布局,在这里通过listview来展现视频列表,有一个progressbar,在加载数据时显示,加载完成后隐藏;一个textview,当没有视频时,显示提示信息,有视频时,不显示。
activity_pager.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@+id/listview" android:layout_width="match_parent" android:layout_height="match_parent"></ListView> <TextView android:visibility="gone" android:textSize="18sp" android:textColor="#000000" android:id="@+id/tv_nomedia" android:text="没有发现视频" android:layout_centerInParent="true" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <ProgressBar android:layout_centerInParent="true" android:id="@+id/pb_loading" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </RelativeLayout>
2、重写VideoPager
在初始化该类时,加载上面的这个布局,并实例化各个组件,然后在初始化数据时,去获取SD卡数据。这里加载数据,要在子线程中去写,不能卸载UI线程中,防止出现ANR错误。通过Handler来实现。
package com.yuanlp.mobileplayer.pager; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Handler; import android.os.Message; import android.provider.MediaStore; import android.view.View; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import com.yuanlp.mobileplayer.R; import com.yuanlp.mobileplayer.base.BasePager; import com.yuanlp.mobileplayer.bean.MediaItem; import com.yuanlp.mobileplayer.utils.LogUtil; import java.util.ArrayList; import java.util.List; /** * Created by 原立鹏 on 2017/7/13. * 本地视频的页面 */ public class VideoPager extends BasePager { private ListView listview; private TextView nomedia; private ProgressBar pb_loding; //保存所有的媒体信息的集合 private List<MediaItem> mediaList; public VideoPager(Context context) { super(context); } private Handler handler=new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (mediaList!=null&&mediaList.size()>0){ //有数据 //设置适配器 }else{ //没有数据 //文本显示 } //progressbar隐藏 } }; /** * 强制子类实现特定的效果 * * @return */ @Override public View initView() { LogUtil.e("本地视频被初始化了"); View view=View.inflate(context, R.layout.video_pager,null); listview= (ListView) view.findViewById(R.id.listview); nomedia= (TextView) view.findViewById(R.id.tv_nomedia); pb_loding= (ProgressBar) view.findViewById(R.id.pb_loading); return view; } @Override public void initData() { super.initData(); LogUtil.e("本地视频页面的数据被初始化了"); //加载本地数据 getDataFromLocal(); } /** * 从本地sd卡获取数据,有2中办法 * 1、遍历sd卡,根据后缀名 * 2、从内容提供者中获取,系统有自己会去扫描所有media信息。 * 3/6.0后的系统,需要加上动态权限 */ private void getDataFromLocal() { mediaList=new ArrayList<>(); new Thread(){ @Override public void run() { super.run(); //根据上下文,去获取内容解析者 ContentResolver resolver = context.getContentResolver(); Uri uri= MediaStore.Video.Media.EXTERNAL_CONTENT_URI; String[] objs={ MediaStore.Video.Media.DISPLAY_NAME, //视频文件名称 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 item=new MediaItem(); String name=cursor.getString(0); //名称 item.setName(name); long duration=cursor.getLong(1); //时长 item.setDuration(duration); long size=cursor.getLong(2); //视频大小 item.setSize(size); String data=cursor.getString(3); //视频的绝对地址 item.setData(data); String artist=cursor.getString(4); //艺术家 item.setArtist(artist); mediaList.add(item); //把每个item数据放到集合中 } cursor.close(); } //发消息,提示加载完media handler.sendEmptyMessage(0); } }.start(); } }
3、音视频的实体类,MediItem,主要是音视频的一些属性形成的类
package com.yuanlp.mobileplayer.bean; /** * Created by 原立鹏 on 2017/7/14. * 代表一个视频和音频 */ public class MediaItem { private String name; private long duration; private long size; private String data; private String artist; 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 + '\'' + '}'; } }
今天主要的地方就是在于标题栏布局中的搜索框那里,还有就是获取本地SD卡的数据方法。