基于ScrollView反弹效果的一些原理(可以看我另外一篇帖子),看了下ListView滚动的一些代码.发现这两者实现方式有些不太一样.
ListView根本没有继承使用View的ScrollBy和ScrollTo,而且它的移动到最上和最下的判断也打不一样,代码如下,截取自AbstractListView的trackMotionScroll(int,int)方法:
final int end = getHeight() - listPadding.bottom;
final int spaceBelow = lastBottom - end;
final int height = getHeight() - mPaddingBottom - mPaddingTop;
if (deltaY < 0) {
deltaY = Math.max(-(height - 1), deltaY);
} else {
deltaY = Math.min(height - 1, deltaY);
}
if (incrementalDeltaY < 0) {
incrementalDeltaY = Math.max(-(height - 1), incrementalDeltaY);
} else {
incrementalDeltaY = Math.min(height - 1, incrementalDeltaY);
}
final int firstPosition = mFirstPosition;
if (firstPosition == 0 && firstTop >= listPadding.top && deltaY >= 0) {
// Don't need to move views down if the top of the first position
// is already visible
return true;
}
if (firstPosition + childCount == mItemCount && lastBottom <= end && deltaY <= 0) {
// Don't need to move views up if the bottom of the last position
// is already visible
return true;
}
这个mFirstPosition是显示的第一个ListView的Item,firstPosition + childCount ==
mItemCount这句话的意思是可以显示最后的childCount条(这是个规律,自己可以算算),在加上lastBottom(也就是最后一个显
示item)比end(也就是ListView的最底部距离最上部的距离)小,一般情况下都是比它大,如图:
,firstTop
>= listPadding.top
和这个相反,也就是第一个item比paddingTop要大,如果ListView不设这个属性的话那就是0了.这里要说下不管是end、
firstTop或者是lastBottom意思都是距离它的最上面的组件或者是parent的距离,不是屏幕.详细的大家可以去看
trackMotionScroll.
我的实现是在基本不破坏原有功能的基础上加入了一些可以继续移动的代码,也就是使用View的scrollBy.
此为抛砖引玉,反弹效果刷新实现有点生硬,大家可以优化它,让它更流畅。
这种效果也应该有人写过,这是我自己的实现。提供附件下载,转载请注明出处。
最近有个项目需要用到实时曲线图,我也上网搜索了一下,最后还是选择使用achartengine这个现成的东西,毕竟自己再canvas一下实在是太麻烦,而且项目时间上也不允许,虽然这个画图引擎比较简单,不过勉强够用了。
下一步问题就来了,我仓促上手,只会画静态图,就是事先定义好几个坐标,然后activity载入的时候折线就已经画好了,可是我的项目要求我每隔一秒种都要把新搜集到的数据添加到图表中去,类似于windows中那个任务管理器里的性能统计图,网上搜来搜去也没找到可用的资源,学习api的文档也没看出门道,只好自己不停地试验了,最后终于用了一种笨方法实现了画动态曲线图,其它的几类图已经没时间在去试了,我想思路应该差不多,回头有空了在说吧。
package com.auto.chat;
import java.util.Timer;
import java.util.TimerTask;
import org.achartengine.ChartFactory;
import org.achartengine.GraphicalView;
import org.achartengine.chart.PointStyle;
import org.achartengine.model.XYMultipleSeriesDataset;
import org.achartengine.model.XYSeries;
import org.achartengine.renderer.XYMultipleSeriesRenderer;
import org.achartengine.renderer.XYSeriesRenderer;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Paint.Align;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.ViewGroup.LayoutParams;
import android.widget.LinearLayout;
public class AChartDemoActivity extends Activity {
private Timer timer = new Timer();
private TimerTask task;
private Handler handler;
private String title = "Signal Strength";
private XYSeries series;
private XYMultipleSeriesDataset mDataset;
private GraphicalView chart;
private XYMultipleSeriesRenderer renderer;
private Context context;
private int addX = -1, addY;
int[] xv = new int[100];
int[] yv = new int[100];
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
context = getApplicationContext();
//这里获得main界面上的布局,下面会把图表画在这个布局里面
LinearLayout layout = (LinearLayout)findViewById(R.id.linearLayout1);
//这个类用来放置曲线上的所有点,是一个点的集合,根据这些点画出曲线
series = new XYSeries(title);
//创建一个数据集的实例,这个数据集将被用来创建图表
mDataset = new XYMultipleSeriesDataset();
//将点集添加到这个数据集中
mDataset.addSeries(series);
//以下都是曲线的样式和属性等等的设置,renderer相当于一个用来给图表做渲染的句柄
int color = Color.GREEN;
PointStyle style = PointStyle.CIRCLE;
renderer = buildRenderer(color, style, true);
//设置好图表的样式
setChartSettings(renderer, "X", "Y", 0, 100, 0, 90, Color.WHITE, Color.WHITE);
//生成图表
chart = ChartFactory.getLineChartView(context, mDataset, renderer);
//将图表添加到布局中去
layout.addView(chart, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
//这里的Handler实例将配合下面的Timer实例,完成定时更新图表的功能
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
//刷新图表
updateChart();
super.handleMessage(msg);
}
};
task = new TimerTask() {
@Override
public void run() {
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
};
timer.schedule(task, 500, 500);
}
@Override
public void onDestroy() {
//当结束程序时关掉Timer
timer.cancel();
super.onDestroy();
}
protected XYMultipleSeriesRenderer buildRenderer(int color, PointStyle style, boolean fill) {
XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer();
//设置图表中曲线本身的样式,包括颜色、点的大小以及线的粗细等
XYSeriesRenderer r = new XYSeriesRenderer();
r.setColor(color);
r.setPointStyle(style);
r.setFillPoints(fill);
r.setLineWidth(3);
renderer.addSeriesRenderer(r);
return renderer;
}
protected void setChartSettings(XYMultipleSeriesRenderer renderer, String xTitle, String yTitle,
double xMin, double xMax, double yMin, double yMax, int axesColor, int labelsColor) {
//有关对图表的渲染可参看api文档
renderer.setChartTitle(title);
renderer.setXTitle(xTitle);
renderer.setYTitle(yTitle);
renderer.setXAxisMin(xMin);
renderer.setXAxisMax(xMax);
renderer.setYAxisMin(yMin);
renderer.setYAxisMax(yMax);
renderer.setAxesColor(axesColor);
renderer.setLabelsColor(labelsColor);
renderer.setShowGrid(true);
renderer.setGridColor(Color.GREEN);
renderer.setXLabels(20);
renderer.setYLabels(10);
renderer.setXTitle("Time");
renderer.setYTitle("dBm");
renderer.setYLabelsAlign(Align.RIGHT);
renderer.setPointSize((float) 2);
renderer.setShowLegend(false);
}
private void updateChart() {
//设置好下一个需要增加的节点
addX = 0;
addY = (int)(Math.random() * 90);
//移除数据集中旧的点集
mDataset.removeSeries(series);
//判断当前点集中到底有多少点,因为屏幕总共只能容纳100个,所以当点数超过100时,长度永远是100
int length = series.getItemCount();
if (length > 100) {
length = 100;
}
//将旧的点集中x和y的数值取出来放入backup中,并且将x的值加1,造成曲线向右平移的效果
for (int i = 0; i < length; i++) {
xv[i] = (int) series.getX(i) + 1;
yv[i] = (int) series.getY(i);
}
//点集先清空,为了做成新的点集而准备
series.clear();
//将新产生的点首先加入到点集中,然后在循环体中将坐标变换后的一系列点都重新加入到点集中
//这里可以试验一下把顺序颠倒过来是什么效果,即先运行循环体,再添加新产生的点
series.add(addX, addY);
for (int k = 0; k < length; k++) {
series.add(xv[k], yv[k]);
}
//在数据集中添加新的点集
mDataset.addSeries(series);
//视图更新,没有这一步,曲线不会呈现动态
//如果在非UI主线程中,需要调用postInvalidate(),具体参考api
chart.invalidate();
}
}
</pre>Layout的xml如下:<p></p><pre name="code" ><?xml version="1.0" encoding="utf-8"?>
<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
<LinearLayout
android:id="@+id/linearLayout1"
android:layout_width="290dp"
android:layout_height="122dp"
android:layout_x="12dp"
android:layout_y="41dp"
android:orientation="vertical" >
</LinearLayout>
<LinearLayout
android:id="@+id/linearLayout2"
android:layout_width="290dp"
android:layout_height="130dp"
android:layout_x="13dp"
android:layout_y="215dp"
android:orientation="vertical" >
</LinearLayout>
</AbsoluteLayout>
sdk的版本是2.2的,效果一般,不过至少能动起来了,上面是一个demo,做其它程序的时候可以写一个继承了View的类,把上面的代码装进去,然后在其它Activity中的任何布局中都可以用addView来添加上这里画出来的曲线了。
直接上视频
http://v.youku.com/v_show/id_XNDA4ODU5NzMy.html
Android源码
package com.eyet.demo.voice; import java.io.IOException; import java.io.InterruptedIOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; import java.util.ArrayList; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.Intent; import android.os.Bundle; import android.speech.RecognizerIntent; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ImageView; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.Toast; public class RecognizerIntent_TestActivity extends Activity { private static final int TIMEOUT = 3000; // Resend timeout (milliseconds) private static final int MAXTRIES = 5; // Maximum retransmissions private static final String REPLY = "OK"; private static final String SERVERIP = "192.168.0.133"; private static final int SERVERPORT = 3693; private static final int VOICE_RECOGNITION_REQUEST_CODE = 1; private String cmd; private ImageView image_but; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); image_but = (ImageView) this.findViewById(R.id.image_but); image_but.setOnClickListener(new myRecognizerIntentListener()); } public void ProtocolHandler() { if (cmd != null && cmd.length() != 0) { try { DatagramSocket socket = new DatagramSocket(); socket.setSoTimeout(TIMEOUT); byte[] bytesToSend = cmd.getBytes(); DatagramPacket sendPacket = new DatagramPacket(bytesToSend, bytesToSend.length, InetAddress.getByName(SERVERIP), SERVERPORT); int len = REPLY.getBytes().length; DatagramPacket receivePacket = new DatagramPacket( new byte[len], len); int tries = 0; // Packets may be lost, so we have to keep trying boolean receivedResponse = false; do { socket.send(sendPacket); // Send the echo string try { socket.receive(receivePacket); if (!receivePacket.getAddress().equals( InetAddress.getByName(SERVERIP))) {// Check // source throw new IOException( "Received packet from an unknown source"); } receivedResponse = true; } catch (InterruptedIOException e) { // We did not get // anything tries += 1; Log.e("Recevice Error", e.getMessage()); } } while ((!receivedResponse) && (tries < MAXTRIES)); if (receivedResponse) { Toast.makeText(RecognizerIntent_TestActivity.this, new String(receivePacket.getData()), Toast.LENGTH_LONG).show(); } else { Toast.makeText(RecognizerIntent_TestActivity.this, "No response -- giving up.", Toast.LENGTH_LONG) .show(); } } catch (IOException e) { Log.e("Socket Error", e.getMessage()); } } } public class myRecognizerIntentListener implements OnClickListener { public void onClick(View v) { try { Intent intent = new Intent( RecognizerIntent.ACTION_RECOGNIZE_SPEECH); // 语言模式和自由形式的语音识别 intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); // 提示语言开始 intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "请开始语音"); // startActivityForResult(intent, VOICE_RECOGNITION_REQUEST_CODE); } catch (ActivityNotFoundException e) { Toast.makeText(RecognizerIntent_TestActivity.this, "找不到语音设备", Toast.LENGTH_LONG).show(); } } } /** * @param voice 语音字符串 * @return 协议命令 | null 没有匹配 */ public String getCmd(String voice){ if(voice == null) return null; String open = "打开"; String close = "关闭"; String result = null; if(voice.indexOf(open) != -1){ if(voice.indexOf("浏览器") != -1){ result = "open_firefox"; }else if(voice.indexOf("百度") != -1){ result = "open_baidu"; }else if(voice.indexOf("记事本") != -1){ result = "open_notepad"; }else if(voice.toLowerCase().indexOf("qq") != -1){ result = "open_qq"; }else if(voice.toLowerCase().indexOf("wps") != -1){ result = "open_wps"; }else if(voice.toLowerCase().indexOf("e") != -1){ result = "open_e"; } }else if(voice.indexOf(close) != -1){ if(voice.indexOf("浏览器") != -1){ result = "close_firefox"; }else if(voice.indexOf("记事本") != -1){ result = "close_notepad"; }else if(voice.toLowerCase().indexOf("wps") != -1){ result = "close_wps"; }else if(voice.indexOf("电脑") != -1){ result = "close_computer"; } } return result; } // 语音结束时的回调函数 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == VOICE_RECOGNITION_REQUEST_CODE && resultCode == RESULT_OK) { // 取得语音的字符 ArrayList<String> results = data .getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS); String str = ""; for (int i = 0; i < results.size(); i++) { str += results.get(i); cmd = getCmd(results.get(i)); if(cmd != null){ break; } } //Toast.makeText(this, cmd+"::==::" + str, Toast.LENGTH_LONG).show(); if(cmd != null){ ProtocolHandler(); } } super.onActivityResult(requestCode, resultCode, data); } }
服务器端
import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.util.HashMap; import java.util.Map; public class RunCmd { private static final int BUFMAX = 255; // Maximum size of echo datagram private static Map<String , Runnable> cmdThreadPool; public static void main(String[] args) { RunCmd rc = new RunCmd(); rc.serverRun(); } public RunCmd() { cmdThreadPool = new HashMap<String , Runnable>(); cmdThreadPool.put("open_firefox", new cmdRunnable("C:\\Program Files\\Mozilla Firefox\\firefox.exe")); cmdThreadPool.put("open_baidu", new cmdRunnable("C:\\Program Files\\Mozilla Firefox\\firefox.exe http://www.baidu.com")); cmdThreadPool.put("close_firefox", new cmdRunnable("taskkill /im firefox.exe")); cmdThreadPool.put("open_notepad", new cmdRunnable("notepad")); cmdThreadPool.put("close_notepad", new cmdRunnable("taskkill /im notepad.exe")); cmdThreadPool.put("open_qq", new cmdRunnable("C:\\Program Files\\Tencent\\QQ\\Bin\\QQProtect\\Bin\\QQProtect.exe")); cmdThreadPool.put("open_wps", new cmdRunnable("C:\\Program Files\\Kingsoft\\WPS Office Personal\\office6\\wps.exe /w")); cmdThreadPool.put("close_wps", new cmdRunnable("taskkill /im wps.exe")); cmdThreadPool.put("open_e", new cmdRunnable("explorer e:")); cmdThreadPool.put("close_computer", new cmdRunnable("shutdown /s /t 0")); } public void serverRun(){ try { DatagramSocket socket = new DatagramSocket(3693); DatagramPacket packet = new DatagramPacket(new byte[BUFMAX], BUFMAX); while (true) { // Run forever, receiving and echoing datagrams socket.receive(packet); // Receive packet from client String cmd = new String(packet.getData(),packet.getOffset() , packet.getLength()); System.out.println(cmd); byte[] replayMsg; if(handlerCmd(cmd)){ replayMsg = new String("OK").getBytes(); }else{ replayMsg = new String("NO").getBytes(); } DatagramPacket replayPacket = new DatagramPacket(replayMsg,replayMsg.length, packet.getAddress() , packet.getPort()); socket.send(replayPacket); // Send the same packet back to client packet.setLength(BUFMAX); // Reset length to avoid shrinking buffer } } catch (IOException e) { System.out.println(e.getMessage()); e.printStackTrace(); } } public boolean handlerCmd(String cmd){ Runnable cmdRun = cmdThreadPool.get(cmd.trim()); if(cmdRun != null){ new Thread(cmdRun).start(); return true; }else{ return false; } } public class cmdRunnable implements Runnable{ private String cmd; public cmdRunnable(String cmd){ this.cmd = cmd; } public void run(){ try { Runtime run = Runtime.getRuntime(); run.exec(cmd); } catch (IOException e) { e.printStackTrace(); } } } /* public class OpenFireFox implements Runnable{ public void run(){ String open_cmd = "C:\\Program Files\\Mozilla Firefox\\firefox.exe"; try { Runtime run = Runtime.getRuntime(); run.exec(open_cmd); } catch (IOException e) { e.printStackTrace(); } } } public class OpenBaiDu implements Runnable{ public void run(){ String open_cmd = "C:\\Program Files\\Mozilla Firefox\\firefox.exe http://www.baidu.com"; try { Runtime run = Runtime.getRuntime(); run.exec(open_cmd); } catch (IOException e) { e.printStackTrace(); } } } public class CloseFireFox implements Runnable{ public void run(){ String close_cmd= "taskkill /im firefox.exe"; try { Runtime run = Runtime.getRuntime(); run.exec(close_cmd); } catch (IOException e) { e.printStackTrace(); } } } public class OpenNotepad implements Runnable{ public void run(){ String open_cmd = "notepad"; try { Runtime run = Runtime.getRuntime(); run.exec(open_cmd); } catch (IOException e) { e.printStackTrace(); } } } public class CloseNotepad implements Runnable{ public void run(){ String close_cmd= "taskkill /im notepad.exe"; try { Runtime run = Runtime.getRuntime(); run.exec(close_cmd); } catch (IOException e) { e.printStackTrace(); } } } public class OpenQQ implements Runnable{ public void run(){ String open_cmd = "C:\\Program Files\\Tencent\\QQ\\Bin\\QQProtect\\Bin\\QQProtect.exe"; try { Runtime run = Runtime.getRuntime(); run.exec(open_cmd); } catch (IOException e) { e.printStackTrace(); } } } public class OpenWPS implements Runnable{ public void run(){ String open_cmd = "C:\\Program Files\\Kingsoft\\WPS Office Personal\\office6\\wps.exe /w"; try { Runtime run = Runtime.getRuntime(); run.exec(open_cmd); } catch (IOException e) { e.printStackTrace(); } } } public class CloseWPS implements Runnable{ public void run(){ String close_cmd= "taskkill /im wps.exe"; try { Runtime run = Runtime.getRuntime(); run.exec(close_cmd); } catch (IOException e) { e.printStackTrace(); } } } public class OpenE implements Runnable{ public void run(){ String open_cmd = "explorer e:"; try { Runtime run = Runtime.getRuntime(); run.exec(open_cmd); } catch (IOException e) { e.printStackTrace(); } } } public class CloseComputer implements Runnable{ public void run(){ String open_cmd = "shutdown /s /t 0"; try { Runtime run = Runtime.getRuntime(); run.exec(open_cmd); } catch (IOException e) { e.printStackTrace(); } } }*/ }