Android实战 – 音心音乐播发器 (主界面实现)

最后更新于:2022-04-01 10:52:40

开发平台 : eclipse , ubuntu ,android  sdk 4.0+ ### 1.背景    主页的设计从上往下依次是滚动广告(ViewFlipper ),分类信息( GridView ),热门榜单( ListView ),整个界面可以滑动,通过ScrollView 包裹,使得整个页面可滑动。    界面展示 :                                                                  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-19_5715764b5b653.jpg)      后发现遇到的问题 , ListView 滑动和 ScrollView 冲突,后整个布局进行了修改,下面的热门榜单是通过 Fragment 实现,在启动的时候进行数据加载。但是有一个问题,如果上面分类信息(GridView)没有实现的话,下面Fragment加载完成并适配好数据的时候,重新定义Fragment 所要适配的布局的高度,进行重新定义,将会蹦到该Fragment上,故在完成GridView的时候,就不在‘蹦’了,如上图所示,效果还不错。 ### 2.广告栏实现(ViewFlipper )    ViewFlipper 的实现 ,布局是自定义布局实现的,包括背景,透明层,左右的文字显示;   (1)ViewFlipper 内容布局实现 ~~~ <?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="200dp" > <ImageView android:id="@+id/iv_list_item_flipper" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="fitXY" android:src="@drawable/moren_big" /> <FrameLayout android:layout_width="match_parent" android:layout_height="40dp" android:layout_alignParentBottom="true" android:background="@color/app_color_borwn" > <TextView android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center|left" android:layout_marginLeft="20dp" android:text="@string/tv_main_xindie" android:textColor="@color/text_color_whrit" /> </FrameLayout> <TextView android:id="@+id/tv_list_item_num" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_margin="10dp" android:text="@string/list_item_flipper_tv" android:textColor="@color/text_color_whrit" /> </RelativeLayout> ~~~    (2)布局实现 ~~~ <ViewFlipper android:id="@+id/main_view_flipper" android:layout_width="match_parent" android:layout_height="200dp" android:layout_marginTop="10dp" > </ViewFlipper> ~~~    (3)初始化VIewFlipper实现 ~~~ private void initViewFlipper() { main_view_flipper.setInAnimation(this, R.drawable.main_fliper_in); main_view_flipper.setOutAnimation(this, R.drawable.main_fliper_out); main_view_flipper.setOnTouchListener(new viewFlipperListener()); for (int i = 0; i < 4; i++) { flipperView = LayoutInflater.from(this).inflate( R.layout.list_item_main_flipper, main_scroll_view, false); flipperTv = (TextView) flipperView .findViewById(R.id.tv_list_item_num); flipperIv = (ImageView) flipperView .findViewById(R.id.iv_list_item_flipper); flipperIv.setTag(VolleyHttpPath.RANDOM_IMAGE_URL); flipperTv.setText((i + 1) + "/4"); ~~~ ~~~ //加载网络图片实现Volley 框架实现 imageListener = ImageLoader.getImageListener(flipperIv, R.drawable.moren, R.drawable.moren_big); main_view_flipper.addView(flipperView); VolleyHttpRequest.Image_Loader(VolleyHttpPath.RANDOM_IMAGE_URL + "?" + i, imageListener); } main_view_flipper.setFlipInterval(7000); main_view_flipper.startFlipping(); } ~~~    (4)两个动画         ---fade in ~~~ <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" > <translate android:duration="800" android:fromXDelta="-100%p" android:toXDelta="0" /> </set> ~~~        ---fade out  ~~~ <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" > <translate android:duration="800" android:fromXDelta="0" android:toXDelta="100%p" /> </set> ~~~     (5)手势控制 ~~~ /** * ViewFlipper 手势控制 * */ private class viewFlipperListener implements OnTouchListener { private int start = 0; @Override public boolean onTouch(View v, MotionEvent event) { gestureDetector.onTouchEvent(event); return true; } } /** * 手势判断 * */ private class gestureDetectorListener extends SimpleOnGestureListener { @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { if (e1.getX() - e2.getX() < 1000) { main_view_flipper.showPrevious(); } return true; } } ~~~ ### 3. 分类信息 (GridView)     静态数据,非网络请求,使用了最简单的数据适配,并添加点击事件;    (1) 布局实现             1)GridView 布局 ~~~ <GridView android:id="@+id/main_gridview" android:layout_width="356dp" android:layout_height="match_parent" android:layout_marginTop="2dp" android:gravity="center" android:horizontalSpacing="0dp" android:numColumns="4" android:scrollbars="none" android:verticalSpacing="0dp" > </GridView> ~~~                 2)Item布局 ~~~ <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="100dp" android:layout_height="100dp" android:layout_margin="1dp" android:background="@color/text_color_whrit" > <ImageView android:id="@+id/iv_item_main_gird" android:layout_width="60dp" android:layout_height="60dp" android:layout_centerInParent="true" android:src="@drawable/logo" /> <TextView android:id="@+id/tv_item_main_grid" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:gravity="center" android:text="@string/list_item_grid_tv" android:textColor="@color/text_color_main" /> </RelativeLayout> ~~~     (2)Adapter实现 ~~~ public class MusicGridAdapter extends BaseAdapter { /** * 主界面 分类信息,适配Adapter */ private SparseArray<String> gridItems; private Context context; private ViewHolder holder = null; private int[] ids = { R.drawable.mingyao, R.drawable.xiaoliang, R.drawable.china, R.drawable.oumei, R.drawable.hongkang, R.drawable.hanguo, R.drawable.riben, R.drawable.yaogun }; public void setGridItems(SparseArray<String> gridItems) { this.gridItems = gridItems; } public void setContext(Context context) { this.context = context; } @Override public int getCount() { return gridItems.size() > 0 ? gridItems.size() : 0; } @Override public String getItem(int position) { // TODO Auto-generated method stub return gridItems.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = LayoutInflater.from(context).inflate( R.layout.list_item_main_gridview, parent, false); holder = new ViewHolder(convertView); } holder.tv_item_main_grid.setText(gridItems.get(position) + ""); holder.iv_item_main_gird.setImageResource(ids[position]); return convertView; } private class ViewHolder { /** * ViewHolder */ public ImageView iv_item_main_gird; public TextView tv_item_main_grid; public ViewHolder(View convertView) { iv_item_main_gird = (ImageView) convertView .findViewById(R.id.iv_item_main_gird); tv_item_main_grid = (TextView) convertView .findViewById(R.id.tv_item_main_grid); } } } ~~~       (3)基本业务实现 ~~~ /** * 初始化 数据 */ private void initData() { gridItems.put(0, getString(R.string.music_fenlei_mingyao)); gridItems.put(1, getString(R.string.msuic_fenlei_xiaoliang)); gridItems.put(2, getString(R.string.music_fenlei_china)); gridItems.put(3, getString(R.string.music_fenlei_oumei)); gridItems.put(4, getString(R.string.music_fenlei_hangkang)); gridItems.put(5, getString(R.string.music_fenlei_hanguo)); gridItems.put(6, getString(R.string.music_fenlei_riben)); gridItems.put(7, getString(R.string.music_fenlei_yaogun)); getFenlei(); } public void getFenlei() { maps.put(getString(R.string.music_fenlei_mingyao), 18); maps.put(getString(R.string.msuic_fenlei_xiaoliang), 23); maps.put(getString(R.string.music_fenlei_china), 5); maps.put(getString(R.string.music_fenlei_oumei), 3); maps.put(getString(R.string.music_fenlei_hangkang), 6); maps.put(getString(R.string.music_fenlei_hanguo), 16); maps.put(getString(R.string.music_fenlei_riben), 17); maps.put(getString(R.string.music_fenlei_yaogun), 19); // 热歌 26 } ~~~ ~~~ // GridView初始化分类信息 MusicGridAdapter musicGridAdapter = new MusicGridAdapter(); musicGridAdapter.setContext(this); musicGridAdapter.setGridItems(gridItems); main_gridview.setAdapter(musicGridAdapter); main_gridview.setOnItemClickListener(new main_gridviewListener()); ~~~     (4)点击监听事件 ~~~ /** * GirdView点击事件 */ private class main_gridviewListener implements OnItemClickListener { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // showToast("点击了乡村 " + maps.get(gridItems.get(position))); Intent intent = new Intent(MainActivity.this, MusicListActivity.class); intent.putExtra("musictype", maps.get(gridItems.get(position))); startActivity(intent); } } ~~~ ### 4.热门榜单实现    使用Fragment 实现 (非v4 包下的),动态的添加到布局中;    实现思路:在MainActivity.xml 给 Fragment一个空白的布局,等待Fragment的填充;   (1)布局实现       --- mainactivity.xml  中给fragment 预留的布局 ~~~ <RelativeLayout android:id="@+id/main_listview_fragement" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="2dp" /> ~~~         ----  fragment.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/main_list_view" android:layout_width="match_parent" android:layout_height="wrap_content" > </ListView> </RelativeLayout> ~~~     ---- list_item.xml ~~~ <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:background="@color/text_color_whrit" android:layout_height="70dp" > <TextView android:id="@+id/list_item_play" android:layout_width="30dp" android:layout_height="50dp" android:layout_alignParentLeft="true" android:layout_centerInParent="true" android:layout_margin="10dp" android:gravity="center" android:textColor="@color/app_color" android:text="@string/main_item_num" /> <TextView android:id="@+id/tv_item_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="18dp" android:layout_toRightOf="@+id/list_item_play" android:maxLines="2" android:text="@string/list_item_song_name" android:textColor="@color/text_color_black" android:textSize="12sp" /> <TextView android:id="@+id/tv_item_singer" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/tv_item_name" android:layout_toRightOf="@+id/list_item_play" android:text="@string/list_item_singer_name" android:textColor="@color/text_color_main" android:textSize="12sp" /> </RelativeLayout> ~~~      (2)Fragment实现 ~~~ package cn.labelnet.fragment; import java.util.ArrayList; import java.util.List; import cn.labelnet.adapter.MusicListAdapter; import cn.labelnet.event.MainToFragmentRefrsh; import cn.labelnet.maskmusic.R; import cn.labelnet.model.MusicModel; import cn.labelnet.net.MusicAsync; import cn.labelnet.net.MusicAsyncHandler; import cn.labelnet.net.MusicRequest; import android.annotation.SuppressLint; import android.app.Fragment; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView; import android.widget.ListView; import android.widget.Toast; public class MainListViewFragment extends Fragment implements MusicAsync { /** * 热歌榜单实现 * MainActivity界面内容填充 */ // 数据请求 private MusicRequest musicRequest = null; private MusicAsyncHandler musicHandler = null; // listview private ListView main_list_view; // adapter private MusicListAdapter musicAdapter; private List<MusicModel> mmsList = new ArrayList<MusicModel>(); // 接口 : 给 Main传递参数 private MainToFragmentRefrsh mainToFragmentRefrsh; // 给Fragment 添加此事件 public void setMainToFragmentRefrsh( MainToFragmentRefrsh mainToFragmentRefrsh) { this.mainToFragmentRefrsh = mainToFragmentRefrsh; } public android.view.View onCreateView(android.view.LayoutInflater inflater, android.view.ViewGroup container, android.os.Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_main_listview_layout, container, false); return view; }; @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // 初始化数据 initData(); // 初始化View initView(view); // 数据请求 musicRequest.requestStringData(5); } /** * 初始化View * * @param view */ private void initView(View view) { main_list_view = (ListView) view.findViewById(R.id.main_list_view); musicAdapter = new MusicListAdapter(mmsList, getActivity()); main_list_view.setAdapter(musicAdapter); main_list_view.setOnItemClickListener(new Main_list_viewListener()); } /** * 初始化数据请求 */ private void initData() { musicHandler = new MusicAsyncHandler(); musicHandler.setMAsync(this); musicRequest = new MusicRequest(); musicRequest.setMusicAsyncHandler(musicHandler); } @Override public void setUserVisibleHint(boolean isVisibleToUser) { if(isVisibleToUser){ musicRequest.requestStringData(5); } } @Override public void onSuccess(List<MusicModel> mms) { // 给MainActivity返回size mainToFragmentRefrsh.changeFragmentHeight(mms.size()); mainToFragmentRefrsh.getMusicModelList(mms); // 请求成功 // String name = mms.get(1).getSingername(); // showToast(name); // Log.i("MaskMusic", name); mmsList.addAll(mms); musicAdapter.notifyDataSetChanged(); } @Override public void onFail(String msg) { // 请求失败 showToast(msg); mainToFragmentRefrsh.onFailListener(); } /** * Toast * * @param msg * 消息 */ private void showToast(String msg) { Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show(); } // listview点击事件 private class Main_list_viewListener implements OnItemClickListener { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { mainToFragmentRefrsh.onListviewOnItemClickListener(position); } } } ~~~     (3)Fragment 中的List 适配器实现 ~~~ package cn.labelnet.adapter; import java.util.List; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.TextView; import cn.labelnet.maskmusic.R; import cn.labelnet.model.MusicModel; public class MusicListAdapter extends BaseAdapter { /** * 主页 listview 音乐列表 * 在MainListViewFragment中实现,实现初始化界面适配 */ private List<MusicModel> list; private Context context; private ViewHolder holder = null; public MusicListAdapter(List<MusicModel> list, Context content) { this.list = list; this.context = content; } @Override public int getCount() { return list.size() > 0 ? list.size() : 0; } @Override public MusicModel getItem(int position) { return list.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = LayoutInflater.from(context).inflate( R.layout.list_item_main_layout, parent, false); holder = new ViewHolder(convertView); } MusicModel musicModel = list.get(position); String songName = musicModel.getSongname() != null ? musicModel .getSongname() : "什么东东"; holder.tv_item_name.setText(songName); String singerName = musicModel.getSingername() != null ? musicModel .getSingername() : "未知"; position += 1; String num = position >= 10 ? (position + "") : ("0" + position); holder.list_item_play.setText(num); holder.tv_item_singer.setText(singerName); return convertView; } class ViewHolder { public TextView tv_item_name; public TextView tv_item_singer, list_item_play; public ViewHolder(View convertView) { tv_item_name = (TextView) convertView .findViewById(R.id.tv_item_name); tv_item_singer = (TextView) convertView .findViewById(R.id.tv_item_singer); list_item_play = (TextView) convertView .findViewById(R.id.list_item_play); } } } ~~~     (4)Fragment 回调事件 (重点)             回调图:                            ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-19_5715764ba8319.jpg)             作用:1)当网络请求成功后,总的条数,从而改变mainactivity.xml中需要填充布局的高度;                        2)回调出所有的数据 models,进行初始化 Service 和通知栏,消除进度条;                        3)点击实现回调,传递item的poistion ,在MainActivity中通过广播通知Service播放这个音乐;                        4)数据请求失败调用,提示和消除进度条; ~~~ public interface MainToFragmentRefrsh { /** * 传条数,改变布局高度 * * @param size */ void changeFragmentHeight(int size); /** * 得到音乐列表 * * @param models */ void getMusicModelList(List<MusicModel> models); /** * ListView点击事件 * * @param postion */ void onListviewOnItemClickListener(int postion); /** * 失败的回调 */ void onFailListener(); } ~~~       (5)改变Fragment填充到的布局高度                 1)工具类 : 像素px和dp的转化 ~~~ public class ViewUtil { /** * dp转px * @param context * @param dpValue * @return */ public static int dip2px(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } /** * px转dp * @param context * @param pxValue * @return */ public static int px2dip(Context context, float pxValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (pxValue / scale + 0.5f); } } ~~~             2)实现动态改变布局高度 ~~~ /** * 作用 : 从 Fragment拿过来 总长度,后 设置 Fragment 所在布局的总高度 */ @Override public void changeFragmentHeight(int size) { // 给listView 设置 高度 main_listview_parames.height = ViewUtil.px2dip(this, ViewUtil.dip2px(this, 70) * size); main_listview_fragement.setLayoutParams(main_listview_parames); } ~~~ ### 5.总结    通过主页的实现,可以通过fragment实现复杂的页面布局(这个布局实现单独的网络请求与数据适配),这是目前我可以想到的并可以实现的;在此应用中请求图片使用了Volley 网络请求框架,使用的是自己进行[二次封装](http://blog.csdn.net/LABLENET/article/details/47859613)的。在此之前有人说,可能发生内存泄露问题,经过使用,并没有发生内存泄露问题,请放心使用。  *使用地址 : [http://blog.csdn.net/LABLENET/article/details/47859613](http://blog.csdn.net/LABLENET/article/details/47859613)*   下篇将进行Service 的实现,在Service 中实现 进行音乐的控制实现;
';