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

开发平台 : eclipse , ubuntu ,android  sdk 4.0+

1.背景

   主页的设计从上往下依次是滚动广告(ViewFlipper ),分类信息( GridView ),热门榜单( ListView ),整个界面可以滑动,通过ScrollView 包裹,使得整个页面可滑动。

   界面展示 : 

                                                                

     后发现遇到的问题 , 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 回调事件 (重点)
            回调图:

                           

            作用: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

  下篇将进行Service 的实现,在Service 中实现 进行音乐的控制实现;

文章导航