开发平台 : eclipse , ubuntu ,android sdk 4.0+
### 1.背景
主页的设计从上往下依次是滚动广告(ViewFlipper ),分类信息( GridView ),热门榜单( ListView ),整个界面可以滑动,通过ScrollView 包裹,使得整个页面可滑动。
界面展示 :
![](https://box.kancloud.cn/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://box.kancloud.cn/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 中实现 进行音乐的控制实现;
- 前言
- Android实战 - 音心音乐播放器 (开启篇)
- Android实战 - 音心音乐播发器 (主界面实现)
- Android实战 - 音心播放器 (Music Service 实现)
- Android实战 - 音心播放器 (通知实现音乐的播放/暂停/下一曲控制)
- Android实战 - 音心播发器 (MusicService ,Notification, MainActivity 总结)
- Android实战 - 音心播放器 (MusicActivity-音乐播放页面界面实现)
- Android实战 - 音心播放器 (MusciActivity-专辑图片获得,基本控制实现)
- Android实战 - 音心播放器(MusicActivity - 歌词实现)
- Android实战 - 音心播放器 (MusicActivity - 倒计时 ,进度条实现)
- Android实战 - 音心播放器 (MusicActivity ,MusicNotification,MusicService总结)
- Android实战 - 音心播放器 (MusicListActivity - 分类信息界面实现)
- Android实战 - 音心播放器 (MusicListActivity - 音乐播放和MainActivity的一个问题)
- Android实战 - 音心播放器 (启动页与社交分享(ShareSDK))
- Android实战 - 音心播放器 (优化Service退出,按两下退出应用实现)
- Android实战 - 音心播放器 (项目总结,应用打包发布)