导言: 该系列文章是翻译Android官方培训中心的课程,供大家参考.
课程一 为各种屏幕尺寸做设计
- Android 1.6 + (示例程序需要 2.1+)
- 了解 Activities 和Fragments
- 有开发Android程序用户界面 经验
- 一些特性需要用到兼容开发包
- 支持多种屏幕
下载示例项目
NewsReader.zip
Android系统用于数百中设备和各种尺寸的屏幕上,从小屏幕手机到大屏幕电视。因此,把您的程序设计为支持各种尺寸的屏幕就显得格外重要,这样更多的用户就可以使用您的程序了。
但是仅仅能兼容各种尺寸的屏幕还是不够的。每种尺寸的屏幕对用户交互都提供了不同的可能性和挑战,因此为了让用户使用起来更加方便并且打动用户,您的程序不仅仅要支持多屏幕还要针对不同尺寸的屏幕去优化用户体验。
这个课程将告诉您如何实现针对各种尺寸的屏幕优化程序界面。
每节课程都有一个示例程序来演示优化多屏幕支持的最佳实践。您可以从右边的链接中下载示例项目并且在您的项目中自由的使用示例代码。
注意 :为了在低于Android 3.0版本上展示Fragment 的使用, 该课程和相关的示例使用了Android兼容开发包 。因此您需要下载并且把兼容开发包类库添加到示例项目中。
在下载的NewsReader项目中的libs文件夹下已经包含了android-support-v4.jar 兼容开发包,如果您使用Eclipse和ADT那么只需要在该文件上点击右键,在弹出的菜单中选择“Build Path ” – “Add to Build Path ” 菜单即可。
课程
支持各种尺寸的屏幕
这节课程将告诉您如何设计适应各种尺寸屏幕的 布局(在View中使用可扩展的尺寸、使用RelativeLayout、使用屏幕尺寸和方向限定符、别名过滤器以及
点9 格式图片
– nine-patch)。
支持各种密度的屏幕
这节课程将告诉您如何设计适应各种像素密度的屏幕(使用密度无关的像素和为不同密度的屏幕提供不同的图片)。
设计自适应的界面交互流程
这节课程将告诉您如何设计分别适应于不同屏幕尺寸和屏幕密度的界面交互流程(运行时检测当前布局、根据当前的布局做交互、处理屏幕配置变化)。
前几天要处理一些图片资料,从看到有人提供了一个批处理的方法,但是只能处理单个目录下的文件命名,我给改动了一下变成可以递归处理很多层目录了,算是半个原创,跟大家分享一下~~
import java.io.File;
/**
* 文件 批量重命名
* @author
*
*/
public class BatchRenameFile {
//输出日志
public static volatile boolean isDebug = false;
private static int j=0;
public static void main(String[] args) {
// String root = "C:/Users/hello/Documents/android/方案/upload/hd_imgfile_rename";//文件夹目录
String root = "C:/Users/hello/Documents/android/方案/upload/test";//文件夹目录
File [] fs = new File(root).listFiles();
String newName = "a";
System.out.println();
BatchRenameFile.isDebug = true;
try {
rename(fs,newName);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 批量 重命名 文件名
* @param files 文件列表(文件夹或文件)
* @param newName 新文件名
* @throws Exception 可能的异常
*/
public static void rename(File [] files ,String newName) throws Exception {
if(files == null || newName == null || newName.trim().length()==0){
return;
}
for(int i=0 ; i< files.length ; i++){
if(files[i] != null && files[i].exists()){
File f = files[i];
int lastIndex = f.getAbsolutePath().lastIndexOf(File.separator);
//父目录
String path = f.getAbsolutePath().substring(0 , lastIndex+1);
if(f.isFile()){
//文件,保持后缀名
String extensions = f.getName().lastIndexOf(".") >0 ?
( f.getName().substring(f.getName().lastIndexOf(".")) ) : "";
f.renameTo(new File(path + newName +j+ extensions));
j++;
if(isDebug){
System.out.println("文件["+f.getName()+"],重命名为["+ path + newName+j+extensions+"]");
}
}else{
//改动处
File [] fs = new File(f.getAbsolutePath()).listFiles();
rename(fs, newName);
//文件夹
// f.renameTo(new File(path + newName));
// if(isDebug){
// System.out.println("文件夹["+f.getName()+"],重命名为["+ path + newName+"]");
// }
}
}
}
}
}
最近由于需要,做了一个音乐播放控制view,在上面需要能
*控制播放
*显示剩余时间
*显示进度(整个view就是一个进度条)
*实现播放暂停,以及ProgressBar的第一二进度功能
开始想到用组合的方式实现,然后重写ProgressBar的方式实现,但是发现很困难而且文字显示也不行
最后只有自己动手写一个新的控制条
主要的原理就是绘制视图的时候控制onDraw,然后在上面画图片和文字
然后在进度变化的时候不断重绘View就可以了
先上图:
其主要的View部分:
package com.hao; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.View.MeasureSpec; import android.view.View.OnClickListener; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; public class ProgressButton extends View{ private Bitmap begin , bm_gray, bm_yellow, bm_second, end_gray, end_yellow, line,begin_gray; private Bitmap pausePressedImg; private Bitmap playPressedImg; private int bitmapWidth = 0 , bitmapHeight = 0, btWidth = 0, btHeight = 0; private int progress = 0, secondProgress = 0; private double perLen = 0, max = 0, maxSize = 0; private OnProgressChanged mOnProgressChanged; private boolean isPlaying = false; private Paint mTextPaint; private String time = "00:00"; private int color = Color.BLUE; public ProgressButton(Context context) { super(context); // TODO Auto-generated constructor stub init(); } public ProgressButton(Context context, AttributeSet attrs, int defStyle){ super(context, attrs, defStyle); init(); } public ProgressButton(Context context, AttributeSet attrs){ super(context, attrs); init(); } private void init(){ begin = drawableToBitmap(getResources().getDrawable(R.drawable.rectangle_left_yellow)); begin_gray = drawableToBitmap(getResources().getDrawable(R.drawable.rectangle_left_gray)); bm_gray = drawableToBitmap(getResources().getDrawable(R.drawable.rectangle_gray)); bm_yellow = drawableToBitmap(getResources().getDrawable(R.drawable.rectangle_yellow)); bm_second = drawableToBitmap(getResources().getDrawable(R.drawable.rectangle_second_yellow)); end_gray = drawableToBitmap(getResources().getDrawable( R.drawable.rectangle_right_gray)); end_yellow = drawableToBitmap(getResources().getDrawable(R.drawable.rectangle_right_yellow)); line = drawableToBitmap(getResources().getDrawable(R.drawable.rectangle_line)); pausePressedImg = BitmapFactory.decodeResource(getResources(), R.drawable.pause_button_pressed); playPressedImg = BitmapFactory.decodeResource(getResources(), R.drawable.play_button_pressed); bitmapHeight = begin.getHeight(); bitmapWidth = begin.getWidth(); btWidth = pausePressedImg.getWidth(); btHeight = pausePressedImg.getHeight(); mTextPaint = new Paint(); mTextPaint.setAntiAlias(true); mTextPaint.setTextSize(14); mTextPaint.setColor(color); setPadding(3, 3, 3, 3); } public static Bitmap drawableToBitmap(Drawable drawable) { int width = drawable.getIntrinsicWidth(); int height = drawable.getIntrinsicHeight(); Bitmap bitmap = Bitmap.createBitmap(width, height, drawable .getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, width, height); drawable.draw(canvas); return bitmap; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub Log.e("*******", "onMeasure"); setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); perLen = maxSize/max; } @Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub Log.e("*******", "onDraw"); int middle1 = (int) (progress*perLen), middle2 = (int) (secondProgress*perLen) ,end = (int) maxSize-4; if(progress == 0 && secondProgress == 0){ //draw background canvas.drawBitmap(begin_gray, new Rect(0,0,bitmapWidth,bitmapHeight), new Rect(0, 0, bitmapWidth, bitmapHeight), null); canvas.drawBitmap(bm_gray, new Rect(0,0,end-middle1,bitmapHeight), new Rect(bitmapWidth, 0, end, bitmapHeight), null); canvas.drawBitmap(end_gray, new Rect(0,0,4,bitmapHeight), new Rect(end, 0, end+4, bitmapHeight), null); //draw button and line canvas.drawBitmap(playPressedImg, new Rect(0, 0, btWidth, btHeight), new Rect(0, 0, btWidth, bitmapHeight), null); canvas.drawBitmap(line, new Rect(0, 0, 2, bitmapHeight), new Rect(btWidth, 0, btWidth+2, bitmapHeight), null); //draw time and line if(time.length() == 5){ canvas.drawBitmap(line, new Rect(0, 0, 2, bitmapHeight), new Rect(end - 50, 0, end-48, bitmapHeight), null); canvas.drawText("-"+time, end-45, bitmapHeight/2+5, mTextPaint); }else{ canvas.drawBitmap(line, new Rect(0, 0, 2, bitmapHeight), new Rect(end - 60, 0, end-58, bitmapHeight), null); canvas.drawText("-"+time, end-55, bitmapHeight/2+5, mTextPaint); } }else{ //begin canvas.drawBitmap(begin, new Rect(0,0,bitmapWidth,bitmapHeight), new Rect(0, 0, bitmapWidth, bitmapHeight), null); canvas.drawBitmap(bm_yellow, new Rect(0,0,middle1-bitmapWidth,bitmapHeight), new Rect(bitmapWidth, 0, middle1, bitmapHeight), null); //middle if(secondProgress != 0 && secondProgress > progress){ canvas.drawBitmap(bm_second, new Rect(0,0,bitmapWidth,bitmapHeight), new Rect(middle1, 0, middle2, bitmapHeight), null); canvas.drawBitmap(bm_gray, new Rect(0,0,bitmapWidth,bitmapHeight), new Rect(middle2, 0, end, bitmapHeight), null); }else{ canvas.drawBitmap(bm_gray, new Rect(0,0,end-middle1,bitmapHeight), new Rect(middle1, 0, end, bitmapHeight), null); } //end canvas.drawBitmap(end_gray, new Rect(0,0,4,bitmapHeight), new Rect(end, 0, end+4, bitmapHeight), null); if(middle2 >= end || middle1 >= end){ canvas.drawBitmap(end_yellow, new Rect(0,0,4,bitmapHeight), new Rect(end, 0, end+4, bitmapHeight), null); } //draw button if(!isPlaying) { canvas.drawBitmap(playPressedImg, new Rect(0, 0, btWidth, btHeight), new Rect(0, 0, btWidth, bitmapHeight), null); }else{ canvas.drawBitmap(pausePressedImg, new Rect(0, 0, btWidth, btHeight), new Rect(0, 0, btWidth, bitmapHeight), null); } //draw line and time canvas.drawBitmap(line, new Rect(0, 0, 2, bitmapHeight), new Rect(btWidth, 0, btWidth+2, bitmapHeight), null); if(time.length() == 5){ canvas.drawBitmap(line, new Rect(0, 0, 2, bitmapHeight), new Rect(end - 50, 0, end-48, bitmapHeight), null); canvas.drawText("-"+time, end-45, bitmapHeight/2+5, mTextPaint); }else{ canvas.drawBitmap(line, new Rect(0, 0, 2, bitmapHeight), new Rect(end - 60, 0, end-58, bitmapHeight), null); canvas.drawText("-"+time, end-55, bitmapHeight/2+5, mTextPaint); } } super.onDraw(canvas); } @Override public boolean onTouchEvent(MotionEvent event) { // TODO Auto-generated method stub //在这里因为要换按钮,故而需要更新整个视图 if(event.getAction() == MotionEvent.ACTION_DOWN){ onClickListener.onClick(this); invalidate(); } return true; } /** * 这个方法必须设置,当播放的时候 * @param isPlaying */ public void setStateChanged(boolean isPlaying){ this.isPlaying = isPlaying; } public void setTextColor(int color){ this.color = color; invalidate(); } /** * Determines the width of this view * @param measureSpec A measureSpec packed into an int * @return The width of the view, honoring constraints from measureSpec */ private int measureWidth(int measureSpec) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { // We were told how big to be result = specSize; } else { result = (int) ((int)max*perLen + getPaddingLeft() + getPaddingRight()); if (specMode == MeasureSpec.AT_MOST) { // Respect AT_MOST value if that was what is called for by measureSpec result = Math.min(result, specSize); } } System.out.println("width:"+result); maxSize = result; return result; } /** * Determines the height of this view * @param measureSpec A measureSpec packed into an int * @return The height of the view, honoring constraints from measureSpec */ private int measureHeight(int measureSpec) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { // We were told how big to be result = specSize; } else { // Measure the text (beware: ascent is a negative number) result = (int) getPaddingTop() + getPaddingBottom() + bitmapHeight; if (specMode == MeasureSpec.AT_MOST) { // Respect AT_MOST value if that was what is called for by measureSpec result = Math.min(result, specSize); } } System.out.println("Height:"+result); return result; } /** * set the time * @param currentTime 当前播放时间 * @param totalTime 总播放时间 */ public void setTime(int currentTime, int totalTime){ int time = totalTime - currentTime; if(time <= 1000){ this.time="00:00"; return; } time/=1000; int minute = time/60; int hour = minute/60; int second = time%60; minute %= 60; if(hour == 0){ this.time = String.format("%02d:%02d", minute,second); }else{ this.time = String.format("%02d:%02d:%02d", hour, minute,second); } } /** * * @param viewWidth 组件的宽度 */ public void setMax(int max){ this.max = max; } public int getMax(){ return (int)max; } /** * 设置第一进度 * @param progress */ public void setProgress(int progress){ if(progress>max){ progress = (int) max; } else if(progress<0){ progress = 0; } if(mOnProgressChanged!=null){ mOnProgressChanged.onProgressUpdated(); } this.progress = progress; invalidate(); } /** * 设置第二进度 * @param secondProgress */ public void setSecondProgress(int secondProgress){ if(secondProgress>max){ secondProgress = (int) max; } else if(secondProgress<0){ secondProgress = 0; } if(mOnProgressChanged!=null){ mOnProgressChanged.onSecondProgressUpdated(); } this.secondProgress = secondProgress; invalidate(); } /** * 设置进度监听器 * @param mOnProgressChanged */ public void setmOnProgressChanged(OnProgressChanged mOnProgressChanged) { this.mOnProgressChanged = mOnProgressChanged; } public interface OnProgressChanged{ void onProgressUpdated(); void onSecondProgressUpdated(); } @Override public void setOnClickListener(OnClickListener l) { // TODO Auto-generated method stub if(l != null) onClickListener = l; super.setOnClickListener(l); } private View.OnClickListener onClickListener; }
控制非常简单,在这里设置了第一和二进度:
package com.hao; import java.util.Timer; import java.util.TimerTask; import com.hao.ProgressView.OnProgressChanged; import android.app.Activity; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.Gravity; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup.LayoutParams; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; public class SeekBarTestActivity extends Activity { ProgressButton bp; int time = 60000 , currentTime = 0; boolean flag = true; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.my_progress_bar); bp = (ProgressButton) findViewById(R.id.pbt); bp.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub System.out.println("main onck"); bp.setStateChanged(flag); flag = !flag; } }); bp.setMax(60000); Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { // TODO Auto-generated method stub handler.sendEmptyMessage(0); } }, 0, 2000); } Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub currentTime +=1000; bp.setProgress(currentTime); bp.setSecondProgress(currentTime+1000); bp.setTime(currentTime, time); } }; }
布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="fill_parent" android:layout_width="fill_parent" android:orientation="vertical"> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="音乐播放"/> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:paddingTop="5dip" android:gravity="center_horizontal"> <com.hao.ProgressButton android:id="@+id/pb1" android:layout_width="300dp" android:layout_height="45dp" android:background="@drawable/button_left_gray_background"/> </LinearLayout> </LinearLayout>
我用到的进度图片都是通过xml定义的在附件中