Android
觸摸屏校準
話接上回,我們發現了手工利用
tslib
校驗觸摸屏的缺點。那麼這一回
我們就來一次稍微高級一點的校驗吧。
我們其實只需要相對的
x,y
以及lcd的
x,y
就可以把校驗係數算出來。這裏要說的是lcd的x,y是絕對的準確的 比如我們要在(50,50)畫一個十字 那麼這個50,50就是我們認爲的絕對座標。我們要的只是從android通過getX()和getY()拿到我們需要的相對座標。
其實一切我們打算做的事情可以都在
InputDevice
裏面做完
下面我把完整之後整個的
InputDevice
貼出來:
*
*
Copyright (
C)
2007 The Android Open
Source
Project
*
*
Licensed under the Apache License,
Version
2.
0 (
the "License"
)
;
*
you may not
use this
file
except
in
compliance with the License.
*
You may obtain a copy
of
the License at
*
*
http:
//www.apache.org/licenses/LICENSE-2.0
*
*
Unless required by applicable law or
agreed to in
writing,
software
*
distributed under the License is distributed on
an "AS IS"
BASIS,
*
WITHOUT WARRANTIES OR
CONDITIONS OF
ANY
KIND
,
either express or
implied.
*
See the License for
the specific language governing permissions
and
*
limitations under the License.
*
/
package
com.
android.
server
;
import
android.
util
.
Log
;
import
android.
view
.
Display;
import
android.
view
.
MotionEvent;
import
android.
view
.
Surface;
import
android.
view
.
WindowManagerPolicy;
import
java
.
io
.
FileInputStream
;
import
java
.
util
.
StringTokenizer
;
import
java
.
io
.
File
;
import
java
.
io
.
FileNotFoundException
;
import
java
.
io
.
FileOutputStream
;
import
java
.
io
.
IOException
;
public
class
InputDevice {
/** Amount that trackball needs to move in order to generate a key event. */
static
final
int
TRACKBALL_MOVEMENT_THRESHOLD =
6;
//once edit
static
final
String
CALIBRATION_FILE =
"/data/etc/pointercal"
;
//edit ends
final
int
id
;
final
int
classes;
final
String
name
;
final
AbsoluteInfo absX;
final
AbsoluteInfo absY;
final
AbsoluteInfo absPressure;
final
AbsoluteInfo absSize;
//once edit
static
TransformInfo tInfo;
//edit ends
long
mDownTime =
0;
int
mMetaKeysState =
0;
static
File
desFile;
final
MotionState mAbs =
new
MotionState(
0,
0)
;
final
MotionState mRel =
new
MotionState(
TRACKBALL_MOVEMENT_THRESHOLD,
TRACKBALL_MOVEMENT_THRESHOLD)
;
static
class
MotionState {
int
xPrecision;
int
yPrecision;
float
xMoveScale;
float
yMoveScale;
MotionEvent currentMove =
null
;
boolean
changed =
false;
boolean
down =
false;
boolean
lastDown =
false;
long
downTime =
0;
int
x =
0;
int
y =
0;
int
pressure =
1;
int
size
=
0;
MotionState(
int
mx,
int
my)
{
xPrecision =
mx;
yPrecision =
my;
xMoveScale =
mx !
=
0 ?
(
1.
0f/
mx)
:
1.
0f;
yMoveScale =
my !
=
0 ?
(
1.
0f/
my)
:
1.
0f;
}
MotionEvent generateMotion(
InputDevice device,
long
curTime,
boolean
isAbs,
Display display,
int
orientation,
int
metaState)
{
if
(
!
changed)
{
return
null
;
}
//once edit
String
prop =
System
.
getProperty
(
"ts.config.calibrate"
,
"noset"
)
;
if
(
prop.
equalsIgnoreCase
(
"start"
)
)
{
Log
.
i(
"XXW prop"
,
prop)
;
Log
.
i(
"XXW"
,
"prop.equalsIgnoreCase start"
)
;
device.
tInfo =
null
;
}
else
if
(
prop.
equalsIgnoreCase
(
"done"
)
)
{
Log
.
i(
"XXW prop"
,
prop)
;
Log
.
i(
"XXW"
,
"prop.equalsIgnoreCase done"
)
;
readCalibrate(
)
;
System
.
setProperty
(
"ts.config.calibrate"
,
"end"
)
;
}
else
{
Log
.
i(
"XXW prop"
,
prop)
;
Log
.
i(
"XXW"
,
"prop.equalsIgnoreCase else"
)
;
}
//edit ends
float
scaledX =
x;
float
scaledY =
y;
float
temp;
float
scaledPressure =
1.
0f;
float
scaledSize =
0;
int
edgeFlags =
0;
if
(
isAbs)
{
int
w =
display.
getWidth
(
)
-
1;
int
h =
display.
getHeight
(
)
-
1;
if
(
orientation =
=
Surface.
ROTATION_90
|
|
orientation =
=
Surface.
ROTATION_270)
{
int
tmp =
w;
w =
h;
h =
tmp;
}
if
(
device.
absX !
=
null
)
{
//once edit
if
(
device.
tInfo !
=
null
)
{
scaledX =
(
device.
tInfo.
x1 *
x +
device.
tInfo.
y1 *
y +
device.
tInfo.
z1)
/
device.
tInfo.
s;
Log
.
i(
"XXW"
,
"x: "
+
x)
;
Log
.
i(
"XXW"
,
"trans x: "
+
scaledX)
;
}
else
//edit ends
scaledX =
(
(
scaledX-
device.
absX.
minValue)
/
device.
absX.
range)
*
w;
}
if
(
device.
absY !
=
null
)
{
//once edit
if
(
device.
tInfo !
=
null
)
{
scaledY =
(
device.
tInfo.
x2 *
x +
device.
tInfo.
y2 *
y +
device.
tInfo.
z2)
/
device.
tInfo.
s;
Log
.
i(
"XXW"
,
"y: "
+
y)
;
Log
.
i(
"XXW"
,
"trans y: "
+
scaledY)
;
}
else
//edit ends
scaledY =
(
(
scaledY-
device.
absY.
minValue)
/
device.
absY.
range)
*
h;
}
if
(
device.
absPressure !
=
null
)
{
scaledPressure =
(
(
pressure-
device.
absPressure.
minValue)
/
(
float
)
device.
absPressure.
range)
;
}
if
(
device.
absSize !
=
null
)
{
scaledSize =
(
(
size
-
device.
absSize.
minValue)
/
(
float
)
device.
absSize.
range)
;
}
switch
(
orientation)
{
case
Surface.
ROTATION_90:
temp =
scaledX;
scaledX =
scaledY;
scaledY =
w-
temp;
break
;
case
Surface.
ROTATION_180:
scaledX =
w-
scaledX;
scaledY =
h-
scaledY;
break
;
case
Surface.
ROTATION_270:
temp =
scaledX;
scaledX =
h-
scaledY;
scaledY =
temp;
break
;
}
if
(
scaledX =
=
0)
{
edgeFlags +
=
MotionEvent.
EDGE_LEFT;
}
else
if
(
scaledX =
=
display.
getWidth
(
)
-
1.
0f)
{
edgeFlags +
=
MotionEvent.
EDGE_RIGHT;
}
if
(
scaledY =
=
0)
{
edgeFlags +
=
MotionEvent.
EDGE_TOP;
}
else
if
(
scaledY =
=
display.
getHeight
(
)
-
1.
0f)
{
edgeFlags +
=
MotionEvent.
EDGE_BOTTOM;
}
}
else
{
scaledX *
=
xMoveScale;
scaledY *
=
yMoveScale;
switch
(
orientation)
{
case
Surface.
ROTATION_90:
temp =
scaledX;
scaledX =
scaledY;
scaledY =
-
temp;
break
;
case
Surface.
ROTATION_180:
scaledX =
-
scaledX;
scaledY =
-
scaledY;
break
;
case
Surface.
ROTATION_270:
temp =
scaledX;
scaledX =
-
scaledY;
scaledY =
temp;
break
;
}
}
changed =
false;
if
(
down !
=
lastDown)
{
int
action
;
lastDown =
down;
if
(
down)
{
action
=
MotionEvent.
ACTION_DOWN;
downTime =
curTime;
}
else
{
action
=
MotionEvent.
ACTION_UP;
}
currentMove =
null
;
if
(
!
isAbs)
{
x =
y =
0;
}
return
MotionEvent.
obtain(
downTime,
curTime,
action
,
scaledX,
scaledY,
scaledPressure,
scaledSize,
metaState,
xPrecision,
yPrecision,
device.
id
,
edgeFlags)
;
}
else
{
if
(
currentMove !
=
null
)
{
if
(
false)
Log
.
i(
"InputDevice"
,
"Adding batch x="
+
scaledX
+
" y="
+
scaledY +
" to "
+
currentMove)
;
currentMove.
addBatch
(
curTime,
scaledX,
scaledY,
scaledPressure,
scaledSize,
metaState)
;
if
(
WindowManagerPolicy.
WATCH_POINTER)
{
Log
.
i(
"KeyInputQueue"
,
"Updating: "
+
currentMove)
;
}
return
null
;
}
MotionEvent me =
MotionEvent.
obtain(
downTime,
curTime,
MotionEvent.
ACTION_MOVE,
scaledX,
scaledY,
scaledPressure,
scaledSize,
metaState,
xPrecision,
yPrecision,
device.
id
,
edgeFlags)
;
currentMove =
me;
return
me;
}
}
}
static
class
AbsoluteInfo {
int
minValue;
int
maxValue;
int
range;
int
flat;
int
fuzz;
}
;
//once edit
static
class
TransformInfo {
float
x1;
float
y1;
float
z1;
float
x2;
float
y2;
float
z2;
float
s;
}
;
//edit ends
InputDevice(
int
_id,
int
_classes,
String
_name,
AbsoluteInfo _absX,
AbsoluteInfo _absY,
AbsoluteInfo _absPressure,
AbsoluteInfo _absSize)
{
id
=
_id;
classes =
_classes;
name
=
_name;
absX =
_absX;
absY =
_absY;
absPressure =
_absPressure;
absSize =
_absSize;
//once edit
desFile =
new
File
(
CALIBRATION_FILE)
;
readCalibrate(
)
;
//edit ends
}
static
void
readCalibrate(
)
{
//xxw added
Log
.
i(
"XXW"
,
"readCalibrate!"
)
;
TransformInfo t =
null
;
try
{
FileInputStream
is =
new
FileInputStream
(
CALIBRATION_FILE)
;
byte
[
]
mBuffer =
new
byte
[
64]
;
int
len =
is.
read
(
mBuffer)
;
is.
close
(
)
;
if
(
len >
0)
{
int
i;
for
(
i =
0 ;
i <
len ;
i+
+
)
{
if
(
mBuffer[
i]
=
=
'/n'
|
|
mBuffer[
i]
=
=
0)
{
break
;
}
}
len =
i;
}
StringTokenizer
st =
new
StringTokenizer
(
new
String
(
mBuffer,
0,
0,
len)
)
;
t =
new
TransformInfo (
)
;
t.
x1 =
Integer
.
parseInt
(
st.
nextToken
(
)
)
;
t.
y1 =
Integer
.
parseInt
(
st.
nextToken
(
)
)
;
t.
z1 =
Integer
.
parseInt
(
st.
nextToken
(
)
)
;
t.
x2 =
Integer
.
parseInt
(
st.
nextToken
(
)
)
;
t.
y2 =
Integer
.
parseInt
(
st.
nextToken
(
)
)
;
t.
z2 =
Integer
.
parseInt
(
st.
nextToken
(
)
)
;
t.
s =
Integer
.
parseInt
(
st.
nextToken
(
)
)
;
}
catch
(
java
.
io
.
FileNotFoundException
e)
{
Log
.
i(
"XXW"
,
"FileNotFound!"
)
;
}
catch
(
java
.
io
.
IOException
e)
{
Log
.
i(
"XXW"
,
"IOException"
)
;
}
tInfo =
t;
Log
.
i(
"XXW"
,
"readCalibrate done!"
)
;
}
}
;
|
與上一次的那個
InputDevice
相比
我將讀取校準文件的代碼單獨的變成一個函數,之所以這麼做
是因爲我們打算不重啓就可以直接讓
android
校準完成。
這
裏其實也沒什麼東西 只是讀取校驗文件 如果讀取成功了就用校驗公式計算出校準後的座標。 爲了避免重啓
所以用了一個系統屬性ts.config.calibrate來決定重新讀取一次文件。當然
當ts.config.calibrate值表明正在校驗的話 就直接傳上來點擊的原始座標而不經過換算。校驗完成之後讀取一次校驗文件
然後將系統屬性變成其他值不再讀取文件。
下面我們就要寫一個
apk
來實現校準了。
這裏
我嘗試了
2
種方法
一種是純
java
的
apk
一種是
jni
的
apk
。其實對於校準來說
上層已經能拿到
x,y
那麼我們取
5
個點就已經可以算出來那
7
個校準值
然後利用
java
存文件了。整個
apk
的java代碼
package
com.
android.
calibrate;
import
java
.
io
.
File
;
import
java
.
io
.
FileNotFoundException
;
import
java
.
io
.
FileOutputStream
;
import
java
.
io
.
IOException
;
import
android.
util
.
Log
;
public
class
Calibrate {
private
calibration cal;
public
Calibrate(
)
{
cal =
new
calibration(
)
;
}
class
calibration {
int
x[
]
=
new
int
[
5]
;
int
xfb[
]
=
new
int
[
5]
;
int
y[
]
=
new
int
[
5]
;
int
yfb[
]
=
new
int
[
5]
;
int
a[
]
=
new
int
[
7]
;
}
;
boolean
perform_calibration(
)
{
Log
.
i(
"XXW"
,
"perform_calibration"
)
;
int
j;
float
n,
x,
y,
x2,
y2,
xy,
z,
zx,
zy;
float
det,
a,
b,
c,
e,
f,
i;
float
scaling =
(
float
)
65536.
0;
// Get sums for matrix
n =
x =
y =
x2 =
y2 =
xy =
0;
for
(
j =
0;
j <
5;
j+
+
)
{
n +
=
1.
0;
x +
=
(
float
)
cal.
x[
j]
;
y +
=
(
float
)
cal.
y[
j]
;
x2 +
=
(
float
)
(
cal.
x[
j]
*
cal.
x[
j]
)
;
y2 +
=
(
float
)
(
cal.
y[
j]
*
cal.
y[
j]
)
;
xy +
=
(
float
)
(
cal.
x[
j]
*
cal.
y[
j]
)
;
}
// Get determinant of matrix -- check if determinant is too small
det =
n *
(
x2 *
y2 -
xy *
xy)
+
x *
(
xy *
y -
x *
y2)
+
y *
(
x *
xy -
y *
x2)
;
if
(
det <
0.
1 &
&
det >
-
0.
1)
{
Log
.
i(
"ts_calibrate: determinant is too small -- %f/n"
,
""
+
det)
;
return
false;
}
// Get elements of inverse matrix
a =
(
x2 *
y2 -
xy *
xy)
/
det;
b =
(
xy *
y -
x *
y2)
/
det;
c =
(
x *
xy -
y *
x2)
/
det;
e =
(
n *
y2 -
y *
y)
/
det;
f =
(
x *
y -
n *
xy)
/
det;
i =
(
n *
x2 -
x *
x)
/
det;
// Get sums for x calibration
z =
zx =
zy =
0;
for
(
j =
0;
j <
5;
j+
+
)
{
z +
=
(
float
)
cal.
xfb[
j]
;
zx +
=
(
float
)
(
cal.
xfb[
j]
*
cal.
x[
j]
)
;
zy +
=
(
float
)
(
cal.
xfb[
j]
*
cal.
y[
j]
)
;
}
// Now multiply out to get the calibration for framebuffer x coord
cal.
a[
0]
=
(
int
)
(
(
a *
z +
b *
zx +
c *
zy)
*
(
scaling)
)
;
cal.
a[
1]
=
(
int
)
(
(
b *
z +
e *
zx +
f *
zy)
*
(
scaling)
)
;
cal.
a[
2]
=
(
int
)
(
(
c *
z +
f *
zx +
i *
zy)
*
(
scaling)
)
;
System
.
out.
printf
(
"%f %f %f/n"
,
(
a *
z +
b *
zx +
c *
zy)
,
(
b *
z +
e *
zx +
f *
zy)
,
(
c
*
z +
f *
zx +
i *
zy)
)
;
// Get sums for y calibration
z =
zx =
zy =
0;
for
(
j =
0;
j <
5;
j+
+
)
{
z +
=
(
float
)
cal.
yfb[
j]
;
zx +
=
(
float
)
(
cal.
yfb[
j]
*
cal.
x[
j]
)
;
zy +
=
(
float
)
(
cal.
yfb[
j]
*
cal.
y[
j]
)
;
}
// Now multiply out to get the calibration for framebuffer y coord
cal.
a[
3]
=
(
int
)
(
(
a *
z +
b *
zx +
c *
zy)
*
(
scaling)
)
;
cal.
a[
4]
=
(
int
)
(
(
b *
z +
e *
zx +
f *
zy)
*
(
scaling)
)
;
cal.
a[
5]
=
(
int
)
(
(
c *
z +
f *
zx +
i *
zy)
*
(
scaling)
)
;
System
.
out.
printf
(
"%f %f %f/n"
,
(
a *
z +
b *
zx +
c *
zy)
,
(
b *
z +
e *
zx +
f *
zy)
,
(
c
*
z +
f *
zx +
i *
zy)
)
;
// If we got here, we're OK, so assign scaling to a[6] and return
cal.
a[
6]
=
(
int
)
scaling;
return
true;
/*
* // This code was here originally to just insert default values
* for(j=0;j<7;j++) { c->a[j]=0; } c->a[1] = c->a[5] = c->a[6] = 1;
* return 1;
*/
}
void
get_sample(
int
index,
int
x1,
int
y1,
int
x,
int
y)
{
Log
.
i(
"XXW"
,
"get_sample"
)
;
cal.
x[
index]
=
x1;
cal.
y[
index]
=
y1;
cal.
xfb[
index]
=
x;
cal.
yfb[
index]
=
y;
}
int
calibrate_main(
)
{
int
result
=
0;
Log
.
i(
"XXW"
,
"calibrate_main"
)
;
if
(
perform_calibration(
)
)
{
String
strPara =
String
.
format
(
"%d %d %d %d %d %d %d"
,
cal.
a[
1]
,
cal.
a[
2]
,
cal.
a[
0]
,
cal.
a[
4]
,
cal.
a[
5]
,
cal.
a[
3]
,
cal.
a[
6]
)
;
boolean
success =
new
File
(
"/data/etc"
)
.
mkdir
(
)
;
if
(
!
success)
{
Log
.
i(
this
.
toString
(
)
,
"no success"
)
;
}
File
desFile =
new
File
(
"/data/etc/pointercal"
)
;
if
(
!
desFile.
exists
(
)
)
try
{
desFile.
createNewFile
(
)
;
}
catch
(
IOException
e1)
{
e1.
printStackTrace
(
)
;
}
FileOutputStream
fos;
try
{
fos =
new
FileOutputStream
(
desFile)
;
byte
[
]
buf =
strPara.
getBytes
(
)
;
int
bytesRead =
buf.
length
;
try
{
fos.
write
(
buf,
0,
bytesRead)
;
fos.
flush
(
)
;
fos.
close
(
)
;
}
catch
(
IOException
e)
{
e.
printStackTrace
(
)
;
}
}
catch
(
FileNotFoundException
e)
{
e.
printStackTrace
(
)
;
}
result
=
0;
}
else
{
result
=
-
1;
}
return
result
;
}
}
|
其實這個就是
tslib
裏那個
ts_calibrate.cpp
的姐妹篇。重要的函數就一個
perform_calibration()
。這裏要注意的是
你必須首先利用
get_sample
讀取
5
個點的值來初始化
cal
然後再調用
calibrate_main
來計算校驗係數。而且那
5
個點的順序爲
左上 右上 右下 左下 中間。這
4
個點基本上應該在屏幕的邊緣附近
否則計算出來的校驗值可能不準。 利用上面的那個類
寫一個
apk
出來跑跑看
流程應該就可以了。
package
com.
android.
calibrate;
import
com.
android.
calibrate.
R;
import
java
.
util
.
ArrayList
;
import
java
.
util
.
List
;
import
android.
app.
Activity
;
import
android.
os.
Bundle;
import
android.
util
.
Log
;
import
android.
view
.
MotionEvent;
import
android.
view
.
View
;
import
android.
view
.
Window
;
import
android.
view
.
WindowManager;
import
android.
view
.
View
.
OnTouchListener;
import
android.
widget.
ImageView
;
import
android.
widget.
Toast;
import
android.
os.
SystemProperties;
public
class
AndroidCalibrate extends
Activity
{
private
Calibrate cal;
private
int
time
;
private
List
<
ImageView
>
list
=
new
ArrayList
<
ImageView
>
(
)
;
int
xList[
]
=
{
50,
270,
270,
50,
160
}
;
int
yList[
]
=
{
50,
350,
350,
50,
200
}
;
static
void
setNotTitle(
Activity
act)
{
act.
requestWindowFeature(
Window
.
FEATURE_NO_TITLE)
;
}
static
void
setFullScreen(
Activity
act)
{
setNotTitle(
act)
;
act.
getWindow
(
)
.
setFlags(
WindowManager.
LayoutParams.
FLAG_FULLSCREEN,
WindowManager.
LayoutParams.
FLAG_FULLSCREEN)
;
}
public
void
onCreate(
Bundle savedInstanceState)
{
super
.
onCreate(
savedInstanceState)
;
setFullScreen(
this
)
;
this
.
setContentView(
R.
layout
.
main)
;
//System.setProperty("ts.config.calibrate", "start");
//String prop = System.getProperty("ts.config.calibrate", null);
SystemProperty.
set
(
"ts.config.calibrate"
,
"start"
)
;
ImageView
image0 =
(
ImageView
)
findViewById(
R.
id
.
crossview_topleft)
;
ImageView
image1 =
(
ImageView
)
findViewById(
R.
id
.
crossview_topright)
;
ImageView
image2 =
(
ImageView
)
findViewById(
R.
id
.
crossview_bottomright)
;
ImageView
image3 =
(
ImageView
)
findViewById(
R.
id
.
crossview_bottomleft)
;
ImageView
image4 =
(
ImageView
)
findViewById(
R.
id
.
crossview_center)
;
image1.
setVisibility(
View
.
INVISIBLE)
;
image2.
setVisibility(
View
.
INVISIBLE)
;
image3.
setVisibility(
View
.
INVISIBLE)
;
image4.
setVisibility(
View
.
INVISIBLE)
;
list
.
clear
(
)
;
list
.
add
(
image0)
;
list
.
add
(
image1)
;
list
.
add
(
image2)
;
list
.
add
(
image3)
;
list
.
add
(
image4)
;
for
(
ImageView
image
:
list
)
{
image
.
setOnTouchListener(
this
.
onTouchListener)
;
}
cal =
new
Calibrate(
)
;
time
=
0;
}
OnTouchListener onTouchListener =
new
OnTouchListener(
)
{
public
boolean
onTouch(
View
v,
MotionEvent event
)
{
Log
.
i(
"Property"
,
SystemProperty.
get
(
"ts.config.calibrate"
,
"noset"
)
)
;
if
(
event
.
getAction
(
)
=
=
MotionEvent.
ACTION_DOWN)
{
Log
.
i(
"XXW"
,
"onTouchListener"
)
;
v.
setVisibility(
View
.
INVISIBLE)
;
time
=
list
.
indexOf
(
(
ImageView
)
v)
;
if
(
time
<
5)
{
if
(
time
+
1 <
5)
list
.
get
(
time
+
1)
.
setVisibility(
View
.
VISIBLE)
;
Log
.
i(
"XXW time onTouchListener"
,
" "
+
time
)
;
cal.
get_sample(
time
,
(
int
)
event
.
getX
(
)
,
(
int
)
event
.
getY
(
)
,
xList[
time
]
,
yList[
time
]
)
;
if
(
time
=
=
4)
{
Log
.
i(
"XXW"
,
"calibrate_main"
)
;
cal.
calibrate_main(
)
;
Toast.
makeText(
getBaseContext(
)
,
"Calibrate Done!"
,
Toast.
LENGTH_SHORT)
.
show
(
)
;
//System.setProperty("ts.config.calibrate", "done");
SystemProperty.
set
(
"ts.config.calibrate"
,
"done"
)
;
AndroidCalibrate.
this
.
finish
(
)
;
}
}
}
return
false;
}
}
;
}
|
|
|