MusicService已经能够接收广播,通过广播接收的内容来做出相应的MediaPlayer对象的处理,包括播放,暂停,停止等,并当MediaPlayer对象的生命周期发生变化的时候,同样通过发送广播,让UI层产生变换。现在后台处理已经写好。下面就来实现前台的Activity。

1·构建UI布局框架##

1.先构建一个RelativeLayout布局,指定一个背景。

2.我的构想是把整个平面分为三部分,第一部分用来调节音量,因为音量调节常用。第二部分是音乐列表。第三部分是音乐控制按钮和音乐进度条。

3.这三部分一步一步的做出来。先做第一部分调节音量的视图。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/relativeLayout1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/bg_mountain"
>
<LinearLayout
android:id="@+id/main_volumeLayout"
android:layout_height="wrap_content"
android:layout_width="fill_parent">
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_weight="1"></LinearLayout>
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_weight="1"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:id="@+id/main_tv_volumeText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="音量:100%"
android:textColor="#ffffffff"
android:textSize="15dp"/>
<SeekBar
android:id="@+id/main_sb_volumebar"
android:layout_width="82dp"
android:layout_height="wrap_content"
android:maxHeight="5dip"
android:minHeight="5dip"
android:progressDrawable="@drawable/seekbar_style"
/>
</LinearLayout>
</LinearLayout>
</RelativeLayout>

这一部分先这样布局,以后如果体验不好再重新修改。

第二部分是歌曲列表的布局

	<ListView
android:id="@+id/main_listview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/linearLayout1"
android:layout_below="@id/main_volumeLayout"
android:fastScrollEnabled="true"
android:layout_marginLeft="10dip"
android:layout_marginRight="10dip"
android:background="@drawable/widget_bg"
android:cacheColorHint="#00000000"></ListView>

第三部分是音乐控制的布局。

               <LinearLayout
android:id="@+id/linearLayout1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="10dip"
android:layout_marginLeft="10dip"
android:layout_marginRight="10dip"
android:background="@drawable/widget_bg"
android:orientation="vertical"
>
<LinearLayout
android:id="@+id/linearLayout2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center">
<ImageButton
android:id="@+id/main_ibtn_pre"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dip"
android:background="@drawable/button_previous"/>
<ImageButton
android:id="@+id/main_ibtn_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dip"
android:background="@drawable/button_play" /> <ImageButton
android:id="@+id/main_ibtn_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dip"
android:background="@drawable/button_stop" /> <ImageButton
android:id="@+id/main_ibtn_next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dip"
android:background="@drawable/button_next" />
</LinearLayout>
<SeekBar
android:id="@+id/main_seekBar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="10dip"
android:paddingRight="10dip" />
<RelativeLayout
android:id="@+id/relativeLayout2"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/main_tv_curtime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:text="00:00" />
<TextView
android:id="@+id/main_tv_totaltime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="00:00" />
</RelativeLayout>
</LinearLayout>

这样整个UI的布局就完成了。

2在之前的MainActivity的基础上继续开发##

先实现最基础的功能,让音乐播放器能够播放,暂停,下一首,上一首,停止。要实现这个功能就要思考,怎么样才能让MusicService能够按照我们UI的状态变化来操纵MediaPlayer对象呢?最直观的一点就是,最起码要让我们的按钮都有响应了,所以要为我们的音乐控制按钮都加上事件监听器,比如,播放按钮,如果触发了播放按钮,就应该让监听器监听到播放按钮被按下,然后我们需要做的就是在用户按下按钮之后,在监听器下做出相应的相应。MusicService里的MediaPlayer对象有绑定了广播接收器。我们可以让按钮按下之后分发相应的广播。通过广播来通知Service。所以,要给各个按钮增加事件监听器,并分发广播。

MainActivity.java

package com.zharma.greatlovemusic;

	import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import com.zharma.data.Music;
import com.zharma.data.MusicList;
import android.support.v7.app.ActionBarActivity;
import android.database.Cursor;
import android.os.Bundle;
import android.os.Handler;
import android.provider.MediaStore;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener; public class MainActivity extends ActionBarActivity { // 显示组件
private TextView tv_current_time;
private TextView tv_total_time;
private ImageButton imgBtn_Previous;
private ImageButton imgBtn_PlayOrPause;
private ImageButton imgBtn_Stop;
private ImageButton imgBtn_Next;
private SeekBar seekBar;
private ListView listView;
private RelativeLayout root_Layout; // 当前歌曲的持续时间和当前位置,作用于进度条
private int total_time;
private int curent_time; //当前歌曲的序号,下标从零开始
private int number; // 播放状态标志位
private int status; //歌曲列表对象
private ArrayList<Music> musicArrayList; //音量控制
private TextView tv_vol;
private SeekBar seekbar_vol; // 进度条控制常量
private static final int PROGRESS_INCREASE = 0;
private static final int PROGRESS_PAUSE = 1;
private static final int PROGRESS_RESET = 2; // 更新进度条的Handler
private Handler seekBarHandler; //睡眠模式相关组件,标识常量
private ImageView iv_sleep;
private Timer timer_sleep ;
private static final boolean NOTSLEEP = false;
private static final boolean ISSLEEP = true; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); findViews();
initMusicList();
initListView();
registerListeners();
checkMusicfile(); startService(new Intent(this, MusicService.class));
// 绑定广播接收器,可以接收广播
bindStatusChangedReceiver();
sendBroadcastOnCommand(MusicService.COMMAND_CHECK_IS_PLAYING);
//初始化进度条的Handler
initSeekBarHandler();
status = MusicService.COMMAND_STOP;
} void findViews() {
listView = (ListView) findViewById(R.id.main_listview);
tv_current_time = (TextView) findViewById(R.id.main_tv_curtime);
tv_total_time = (TextView) findViewById(R.id.main_tv_totaltime);
imgBtn_Previous = (ImageButton) findViewById(R.id.main_ibtn_pre);
imgBtn_PlayOrPause = (ImageButton) findViewById(R.id.main_ibtn_play);
imgBtn_Previous = (ImageButton) findViewById(R.id.main_ibtn_pre);
imgBtn_Next = (ImageButton) findViewById(R.id.main_ibtn_next);
imgBtn_Stop = (ImageButton) findViewById(R.id.main_ibtn_stop);
seekBar = (SeekBar) findViewById(R.id.main_seekBar);
root_Layout = (RelativeLayout) findViewById(R.id.relativeLayout1); tv_vol = (TextView)findViewById(R.id.main_tv_volumeText);
seekbar_vol = (SeekBar)findViewById(R.id.main_sb_volumebar);
iv_sleep = (ImageView)findViewById(R.id.main_iv_sleep);
} /**初始化音乐列表对象*/
private void initMusicList() {
musicArrayList = MusicList.getMusicList();
//避免重复添加音乐
if(musicArrayList.isEmpty())
{
Cursor mMusicCursor = this.getContentResolver().query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null,
MediaStore.Audio.AudioColumns.TITLE);
int indexTitle = mMusicCursor.getColumnIndex(MediaStore.Audio.AudioColumns.TITLE);
int indexArtist = mMusicCursor.getColumnIndex(MediaStore.Audio.AudioColumns.ARTIST);
int indexTotalTime = mMusicCursor.getColumnIndex(MediaStore.Audio.AudioColumns.DURATION);
int indexPath = mMusicCursor.getColumnIndex(MediaStore.Audio.AudioColumns.DATA); /**通过mMusicCursor游标遍历数据库,并将Music类对象加载带ArrayList中*/
for (mMusicCursor.moveToFirst(); !mMusicCursor.isAfterLast(); mMusicCursor
.moveToNext()) {
String strTitle = mMusicCursor.getString(indexTitle);
String strArtist = mMusicCursor.getString(indexArtist);
String strTotoalTime = mMusicCursor.getString(indexTotalTime);
String strPath = mMusicCursor.getString(indexPath); if (strArtist.equals("<unknown>"))
strArtist = "无艺术家";
Music music = new Music(strTitle, strArtist, strPath, strTotoalTime);
musicArrayList.add(music);
}
}
}
/**设置适配器并初始化listView*/
private void initListView() {
List<Map<String, String>> list_map = new ArrayList<Map<String, String>>();
HashMap<String, String> map;
SimpleAdapter simpleAdapter;
for (Music music : musicArrayList) {
map = new HashMap<String, String>();
map.put("musicName", music.getMusicName());
map.put("musicArtist", music.getMusicArtist());
list_map.add(map);
} String[] from = new String[] { "musicName", "musicArtist" };
int[] to = { R.id.listview_tv_title_item, R.id.listview_tv_artist_item }; simpleAdapter = new SimpleAdapter(this, list_map, R.layout.listview,from, to);
listView.setAdapter(simpleAdapter);
} private void registerListeners() {
imgBtn_Previous.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
sendBroadcastOnCommand(MusicService.COMMAND_PREVIOUS);
}
}); imgBtn_PlayOrPause.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
switch (status) {
case MusicService.STATUS_PLAYING:
sendBroadcastOnCommand(MusicService.COMMAND_PAUSE);
break;
case MusicService.STATUS_PAUSED:
sendBroadcastOnCommand(MusicService.COMMAND_RESUME);
break;
case MusicService.COMMAND_STOP:
sendBroadcastOnCommand(MusicService.COMMAND_PLAY);
break;
default:
break;
}
}
}); imgBtn_Stop.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
sendBroadcastOnCommand(MusicService.COMMAND_STOP);
}
}); imgBtn_Next.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
sendBroadcastOnCommand(MusicService.COMMAND_NEXT);
}
}); listView.setOnItemClickListener(new OnItemClickListener() { @Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
number = position;
sendBroadcastOnCommand(MusicService.COMMAND_PLAY);
}
}); seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { @Override
public void onStopTrackingTouch(SeekBar seekBar) {
if (status == MusicService.STATUS_PLAYING) {
// 发送广播给MusicService,执行跳转
sendBroadcastOnCommand(MusicService.COMMAND_SEEK_TO);
// 进度条恢复移动
seekBarHandler.sendEmptyMessageDelayed(PROGRESS_INCREASE,
1000);
}
} @Override
public void onStartTrackingTouch(SeekBar seekBar) {
// 进度条暂停移动
seekBarHandler.sendEmptyMessage(PROGRESS_PAUSE);
} @Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
if (status != MusicService.STATUS_STOPPED) {
curent_time = progress;
// 更新文本
tv_current_time.setText(formatTime(curent_time));
}
}
}); }
}

现在主体已经写好,后面就是具体的各个实现方法。把发送广播的方法与格式化时间的方法实现如下:

            /** 发送命令,控制音乐播放。参数定义在MusicService类中 */
private void sendBroadcastOnCommand(int command) {
Intent intent = new Intent(MusicService.BROADCAST_MUSICSERVICE_CONTROL);
intent.putExtra("command", command);
// 根据不同命令,封装不同的数据
switch (command) {
case MusicService.COMMAND_PLAY:
intent.putExtra("number", number);
break;
case MusicService.COMMAND_SEEK_TO:
intent.putExtra("time", curent_time);
break;
case MusicService.COMMAND_PREVIOUS:
case MusicService.COMMAND_NEXT:
case MusicService.COMMAND_PAUSE:
case MusicService.COMMAND_STOP:
case MusicService.COMMAND_RESUME:
default:
break;
}
sendBroadcast(intent);
} /**如果列表没有歌曲,则播放按钮不可用,并提醒用户*/
private void checkMusicfile()
{
if (musicArrayList.isEmpty()) {
imgBtn_Next.setEnabled(false);
imgBtn_PlayOrPause.setEnabled(false);
imgBtn_Previous.setEnabled(false);
imgBtn_Stop.setEnabled(false);
Toast.makeText(getApplicationContext(), "当前没有歌曲文件",Toast.LENGTH_SHORT).show();
} else {
imgBtn_Next.setEnabled(true);
imgBtn_PlayOrPause.setEnabled(true);
imgBtn_Previous.setEnabled(true);
imgBtn_Stop.setEnabled(true);
}
} /** 绑定广播接收器 */
private void bindStatusChangedReceiver() {
receiver = new StatusChangedReceiver();
IntentFilter filter = new IntentFilter(
MusicService.BROADCAST_MUSICSERVICE_UPDATE_STATUS);
registerReceiver(receiver, filter);
} /** 内部类,用于播放器状态更新的接收广播 */
class StatusChangedReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
// 获取播放器状态
status = intent.getIntExtra("status", -1);
switch (status) {
case MusicService.STATUS_PLAYING:
String musicName = intent.getStringExtra("musicName");
String musicArtist = intent.getStringExtra("musicArtist");
seekBarHandler.removeMessages(PROGRESS_INCREASE);
curent_time = intent.getIntExtra("time", 0);
total_time = intent.getIntExtra("duration", 0);
number = intent.getIntExtra("number", number);
listView.setSelection(number); seekBar.setProgress(curent_time);
seekBar.setMax(total_time);
seekBarHandler.sendEmptyMessageDelayed(PROGRESS_INCREASE, 1000); tv_total_time.setText(formatTime(total_time));
imgBtn_PlayOrPause.setBackgroundResource(R.drawable.pause); // 设置Activity的标题栏文字,提示正在播放的歌曲
MainActivity.this.setTitle("正在播放:" + musicName + " "+ musicArtist);
break;
case MusicService.STATUS_PAUSED:
seekBarHandler.sendEmptyMessage(PROGRESS_PAUSE);
String string = MainActivity.this.getTitle().toString().replace("正在播放:", "已暂停:");
MainActivity.this.setTitle(string);
imgBtn_PlayOrPause.setBackgroundResource(R.drawable.play);
break;
case MusicService.STATUS_STOPPED:
curent_time = 0;
total_time = 0;
tv_current_time.setText(formatTime(curent_time));
tv_total_time.setText(formatTime(total_time));
seekBarHandler.sendEmptyMessage(PROGRESS_RESET);
MainActivity.this.setTitle("GracePlayer");
imgBtn_PlayOrPause.setBackgroundResource(R.drawable.play);
break;
case MusicService.STATUS_COMPLETED:
number = intent.getIntExtra("number", 0);
//顺序模式:到达列表末端时发送停止命令,否则播放下一首
if(playmode == MainActivity.MODE_LIST_SEQUENCE)
{
if(number == MusicList.getMusicList().size()-1)
sendBroadcastOnCommand(MusicService.STATUS_STOPPED);
else
sendBroadcastOnCommand(MusicService.COMMAND_NEXT);
}
//单曲循环
else if(playmode == MainActivity.MODE_SINGLE_CYCLE)
sendBroadcastOnCommand(MusicService.COMMAND_PLAY);
//列表循环:到达列表末端时,把要播放的音乐设置为第一首
else if(playmode == MainActivity.MODE_LIST_CYCLE)
{
//然后发送播放命令。
if(number == musicArrayList.size()-1)
{
number = 0;
sendBroadcastOnCommand(MusicService.COMMAND_PLAY);
}
else sendBroadcastOnCommand(MusicService.COMMAND_NEXT);
}
//随机播放
else if (playmode == MainActivity.MODE_LIST_RANDOM)
{
Random random = new Random();
int randomnum = random.nextInt(listView.getCount());
number = randomnum;
sendBroadcastOnCommand(MusicService.COMMAND_PLAY);
} seekBarHandler.sendEmptyMessage(PROGRESS_RESET);
MainActivity.this.setTitle("GracePlayer");
imgBtn_PlayOrPause.setBackgroundResource(R.drawable.play);
break;
default:
break;
}
}
} private void initSeekBarHandler() {
seekBarHandler = new Handler() {
public void handleMessage(Message msg) {
super.handleMessage(msg); switch (msg.what) {
case PROGRESS_INCREASE:
if (seekBar.getProgress() < total_time) {
// 进度条前进1秒
seekBar.incrementProgressBy(1000);
seekBarHandler.sendEmptyMessageDelayed(
PROGRESS_INCREASE, 1000);
// 修改显示当前进度的文本
tv_current_time.setText(formatTime(curent_time));
curent_time += 1000;
}
break;
case PROGRESS_PAUSE:
seekBarHandler.removeMessages(PROGRESS_INCREASE);
break;
case PROGRESS_RESET:
// 重置进度条界面
seekBarHandler.removeMessages(PROGRESS_INCREASE);
seekBar.setProgress(0);
tv_current_time.setText("00:00");
break;
}
}
};
}

3·AndroidManifest.xml文件配置##

最后还要把我们的Service加到配置文件里。

   <service
android:name="com.zharma.greatlovemusic.MusicService"
android:exported="true" >
<intent-filter>
<action android:name="VideoService.START_Video_SERVICE" /> <category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>

到目前为止,这个播放器已经实现了基本的音乐播放功能,后面有时间就会再加一些网络歌词获取,放到一个Activity里。搞个华丽的侧滑界面。弄个睡眠模式,播放模式之类的东西,让这个播放器看起来更正儿八经。

Android小玩意儿-- 从头开发一个正经的MusicPlayer(三)的更多相关文章

  1. Android小玩意儿-- 从头开发一个正经的MusicPlayer(一)

    之前从未接触过音乐播放器这块东西的开发.今天偶然想做一个自己的音乐播放器.算是练练手.既然要做,就要做一个正儿八经的App.很多网上的资料也是模模糊糊,不是很全,现在开始,自己摸索着尝试着一步一步的做 ...

  2. Android小玩意儿-- 从头开发一个正经的MusicPlayer(二)

    1·在Service中实例化MusicPlayer,实现对整个播放过程的控制 上一次做到了找到音乐数据,并封装成对象装在ArrayList里,把数据的信息显示在UI上.下面一个阶段就要开始真正的音乐播 ...

  3. 微信小程序实战--开发一个简单的快递单号查询

    功能如图: 虽然工作中只负责小程序后台开发,但是还是小程序开发产生了浓厚的兴趣,官方文档也是超级详细了 这里就简单做一个快递单号的查询: 新建一个page: 接着就可以写wxml了.这里用一个简单的i ...

  4. 微信小程序:开发之前要知道的三件事

    前言 微信之父张小龙在年初的那次演讲中曾表示:"我自己是很多年的程序员,我觉得我们应该为开发的团队做一些事情".几个月后,微信正式推出微信应用号(即微信小程序),在互联网中掀起了又 ...

  5. 用Visual Studio 2015 编写 MASM 汇编程序(二)从头开发一个Win32汇编程序

    一,建立一个VC的控制台类型的空工程: 1,从VS菜单中选择“文件”->“新建”->“项目”. 2,在新建项目中选择:“Visual c++”->"Win32"- ...

  6. Android | 教你如何用代码开发一个拍照翻译小程序

    引子   想必有很多小伙伴喜欢外出旅游,能去海外玩一圈那是更好不过了,旅游前大家一定会对吃.穿.住.行.游玩路线做各种攻略,然后满怀期待的出发- 想象中的旅游   出发前,想象中的旅游目的地可能有漂亮 ...

  7. Android | 教你如何用华为HMS MLKit 图像分割 SDK开发一个证件照DIY小程序

    Android | 教你如何用华为HMS MLKit 图像分割 SDK开发一个证件照DIY小程序 引子   上期给大家介绍了如何使用如何用华为HMS MLKit SDK 三十分钟在安卓上开发一个微笑抓 ...

  8. 「1.0」一个人开发一个App,小程序从0到1,起航了

    古有,秦.齐.楚.赵.魏.韩.燕七国争雄:今有,微信.QQ.百度.支付宝.钉钉.头条.抖音七台争霸.古有,白起.李牧.王翦.孙膑.庞涓.赵奢.廉颇驰骋疆场:今有程序员1,程序员2,程序员3…编写代码. ...

  9. Android | 教你如何用华为HMS MLKit SDK 三十分钟在安卓上开发一个微笑抓拍神器

    Android | 只要三十分钟就可以在手机上开发一个微笑抓拍神器!!! 前言 前段时间Richard Yu在发布会上给大家介绍了华为HMS Core4.0,回顾发布会信息请戳: 华为面向全球发布HM ...

随机推荐

  1. git push不成功 insufficient permission for adding an object to repository database

    这常见于多用户. 1. 确保所有用户在同一个组: 2. 确保所有文件被组可读写. 当多个用户各自进行了push操作后,object目录下的文件可能各自属于各个用户.

  2. BZOJ_2443_[Usaco2011 Open]奇数度数 _并查集+树形DP

    BZOJ_2443_[Usaco2011 Open]奇数度数 _并查集. Description 奶牛们遭到了进攻!在他们的共和国里,有N(1 <= N <=50,000)个城市,由M(1 ...

  3. BZOJ_3073_[Pa2011]Journeys_线段树优化建图+BFS

    BZOJ_3073_[Pa2011]Journeys_线段树优化建图+BFS Description Seter建造了一个很大的星球,他准备建造N个国家和无数双向道路.N个国家很快建造好了,用1..N ...

  4. 「HNSDFZ暑期集训 测试1」「LuoguT36488」 连连看

    题目描述 给定一个n × m的矩形地图,每个各自上可能为空,可能有牌,牌上有一个数字. 对于两张同样数字的牌,如果我们可以在地图上用不超过三根水平或竖直,在地图界内,且不经过其他牌的线段将两张牌连起来 ...

  5. 【AHOI2009】中国象棋

    [题目链接] 点击打开链接 [算法] 动态规划 f[i][j][k]表示前i行,有j列放了1个,有k列放了两个 分六种情况讨论即可 [代码] #include<bits/stdc++.h> ...

  6. c++常见面试题30道

    1.new.delete.malloc.free关系 delete会调用对象的析构函数,和new对应free只会释放内存,new调用构造函数.malloc与free是C++/C语言的标准库函数,new ...

  7. 2.while循环 编码的初识,逻辑运算符 格式化输出

    while循环 循环 while True: # while 是关键字 条件 print('精忠报国') print('团结就是力量') print('北京欢迎你') print('葫芦爷爷救娃娃') ...

  8. [USACO10MAR]伟大的奶牛聚集Great Cow Gat…【树形dp】By cellur925

    题目传送门 首先这道题是在树上进行的,然后求最小的不方便程度,比较符合dp的性质,那么我们就可以搞一搞树形dp. 设计状态:f[i]表示以i作为聚集地的最小不方便程度.那么我们还需要各点间的距离,但是 ...

  9. PHP 使用 header 方式实现文件下载功能

    header() 函数向客户端发送原始的 HTTP 报头. 下载文件要用的的请求头: header("Content-type:application/octet-stream") ...

  10. 如何让Android微博个人详情页滚动到顶部

    版权声明:本文为xing_star原创文章,转载请注明出处! 本文同步自http://javaexception.com/archives/103 个人详情页滑动到顶部 最近产品提了个新需求,需要实现 ...