您的位置:首页 > 移动开发 > Android开发

给android增加屏幕校准

2016-05-26 17:59 423 查看
Android原始版本里是没有屏幕校准功能的,tp坐标到lcd坐标是完全按照线性关系来转换的。例如,tp坐标是(Xt ,Yt )分辨率是(Wt x
Ht ),lcd坐标是(X,Y),分辨率是(W x H),则 X=(Xt *W)/Wt, Y=(Yt *H)/Ht 。但是一般触摸屏不是完全线性的,自然转换关系也就不一样了,好在有tslib,能帮我们解决这个问题。但是android里没有tslib,我们也不需要完全将tslib移植过来,只需要其中根据采样点生成转换矩阵的部分,这部分是由ts_calibrate.c文件中的perform_calibration()函数来实现的。所以只需要将该函数移植过来就可以。这里将该函数及用到的数据结构代码贴出来如下:

typedef struct{

  int x[5], xfb[5];//x[5],y[5]是分别是tp五个点的x,y坐标,xfb[5],yfb[5]是lcd五个点的x,y坐标。

  int y[5], yfb[5];

  int a[7];//a[7]是生成的7个转换参数,tp到lcd坐标转换方程是: xfb = (x*a[1]+y*a[2]+a[0])/a[6]

}calibration;      //  yfb = (x*a[4]+y*a[5]+a[3])/a[6]

int perform_calibration(calibration *cal) {

          int j;

         float n, x, y, x2, y2, xy, z, zx, zy;

         float det, a, b, c, e, f, i;

         float scaling = 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) {

                 printf("ts_calibrate: determinant is too small -- %f/n",det);

                 return 0;

         }

 

 // 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));

         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));

         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 1;



有了上面的基础,接下来就是将上面的代码移植到android中并在setting里面添加屏幕校准入口,要完成这些,需要修改的文件有:

1. frameworks/base/services/Java/com/android/server/InputDevice.java

2. packages/apps/Settings/AndroidManifest.xml

3. packages/apps/Settings/res/xml/settings.xml

另外在Setting源码目录里再添加一个Calibration.java文件。

 

frameworks/base/services/java/com/android/server/InputDevice.java修改的地方如下(红色 表示添加,蓝色 表示删除):

public class InputDevice {

     static final boolean DEBUG_POINTERS = false;

     static final boolean DEBUG_HACKS = false;

 

     /** Amount that trackball needs to move in order to generate a key event. */

     static final int TRACKBALL_MOVEMENT_THRESHOLD = 6;

 

     /** Maximum number of pointers we will track and report. */

     static final int MAX_POINTERS = 10;

 
     static final String CALIBRATION_FILE="/data/pointercal"; 

 

     final int id;

     final int classes;

     final String name;

     final AbsoluteInfo absX;

     final AbsoluteInfo absY;

     final AbsoluteInfo absPressure;

     final AbsoluteInfo absSize;

 

     ....................

 

            final AbsoluteInfo absX = device.absX;

             final AbsoluteInfo absY = device.absY;

             final AbsoluteInfo absPressure = device.absPressure;

             final AbsoluteInfo absSize = device.absSize;

 
             float tmpX = 0;    

             float tmpY = 0;

             String prop = SystemProperties.get("sys.config.calibrate", "noset");

 

             if ( !prop.equalsIgnoreCase("loaded") )

             {

                     if ( prop.equalsIgnoreCase("start") )

                     {

                         TransformInfo.xs =  0;

                         TransformInfo.ys =  0;

                     }

                     else if ( prop.equalsIgnoreCase("done") )

                     {

                         readCalibrate();

                     }

 

                     if ( TransformInfo.xs == 0 && absX != null )

                     {

                         TransformInfo.x1 = w;

                         TransformInfo.y1 = 0;

                         TransformInfo.z1 = 0-absX.minValue*w;

                         TransformInfo.xs =  absX.range;

                     }

                     if ( TransformInfo.ys == 0 && absY != null )

                     {

                         TransformInfo.x2 = 0;

                         TransformInfo.y2 = h;

                         TransformInfo.z2 = 0-absY.minValue*h;

                         TransformInfo.ys =  absY.range;

                     }

                     SystemProperties.set("sys.config.calibrate", "loaded");

             }

 

            for (int i=0; i<numPointers; i++) {

                 final int j = i * MotionEvent.NUM_SAMPLE_DATA;

                 tmpX = reportData[j + MotionEvent.SAMPLE_X];

                 tmpY = reportData[j + MotionEvent.SAMPLE_Y];
                 if (absX != null ) {
                    reportData[j + MotionEvent.SAMPLE_X] =

                             ((reportData[j + MotionEvent.SAMPLE_X]-absX.minValue)

                                 / absX.range) * w; 

                      reportData[j + MotionEvent.SAMPLE_X] = (TransformInfo.x1 * tmpX + TransformInfo.y1 * tmpY + TransformInfo.z1) / TransformInfo.xs;
                 }

                 if (absY != null ) {
                    reportData[j + MotionEvent.SAMPLE_Y] =

                             ((reportData[j + MotionEvent.SAMPLE_Y]-absY.minValue)

                                 / absY.range) * h;

                      reportData[j + MotionEvent.SAMPLE_Y] = (TransformInfo.x2 * tmpX + TransformInfo.y2 * tmpY + TransformInfo.z2) / TransformInfo.ys;
                 }

                 if (absPressure != null) {

                     reportData[j + MotionEvent.SAMPLE_PRESSURE] =

                             ((reportData[j + MotionEvent.SAMPLE_PRESSURE]-absPressure.minValue)

                                 / (float)absPressure.range);

                 }

                 if (absSize != null) {

                     reportData[j + MotionEvent.SAMPLE_SIZE] =

                             ((reportData[j + MotionEvent.SAMPLE_SIZE]-absSize.minValue)

                                 / (float)absSize.range);

                 }

 

 

    ..................................

 

 

     static class AbsoluteInfo {

         int minValue;

         int maxValue;

         int range;

      int flat;

         int fuzz;

      };

 
      static class TransformInfo {

         static int x1;

         static int y1;

         static int z1;

         static int x2;

         static int y2;

         static int z2;

         static int xs;

         static int ys;

     };
 

     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;

 
         TransformInfo.xs =  0;

         TransformInfo.ys =  0;

         readCalibrate();

 } 

    static void readCalibrate(){

             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, len));

 

                 TransformInfo.x1 = Integer.parseInt(st.nextToken());

                 TransformInfo.y1 = Integer.parseInt(st.nextToken());

                 TransformInfo.z1 = Integer.parseInt(st.nextToken());

                 TransformInfo.x2 = Integer.parseInt(st.nextToken());

                 TransformInfo.y2 = Integer.parseInt(st.nextToken());

                 TransformInfo.z2 = Integer.parseInt(st.nextToken());

                 TransformInfo.xs = Integer.parseInt(st.nextToken());

                 TransformInfo.ys = TransformInfo.xs;

                 SystemProperties.set("sys.config.calibrate", "loaded");

             } catch (java.io.FileNotFoundException e) {

                 Log.i("InputDevice", "calibration file not found exception");

             } catch (java.io.IOException e) {

                 Log.i("InputDevice", "io exception");

             } catch (java.lang.NumberFormatException e) {

                Log.i("InputDevice", "number format exception");

             }

   }

};

 

 

packages/apps/Settings/AndroidManifest.xml文件修改地方如下:

 

         <activity android:name=".fuelgauge.PowerUsageDetail"

                 android:label="@string/details_title">

             <intent-filter>

                 <action android:name="android.intent.action.MAIN" />

                 <category android:name="android.intent.category.DEFAULT" />

             </intent-filter>

         </activity>

 

         <activity android:name="Calibration" 

                 android:label="@string/screen_calibration_settings">

               <intent-filter>

                   <action android:name="android.intent.action.MAIN" />

                   <category android:name="android.intent.category.DEFAULT" />

               </intent-filter>

         </activity> 

 

         <receiver android:name=".widget.SettingsAppWidgetProvider" android:label="@string/gadget_title">

             <intent-filter>

                 <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />

 

 

packages/apps/Settings/res/xml/settings.xml文件修改地方如下:

 

        <com.android.settings.IconPreferenceScreen

             settings:icon="@drawable/ic_settings_date_time"

             android:title="@string/date_and_time_settings_title">

             <intent

                 android:action="android.intent.action.MAIN"

                 android:targetPackage="com.android.settings"

                 android:targetClass="com.android.settings.DateTimeSettings" />

         </com.android.settings.IconPreferenceScreen>

 
         <!-- touchscreen Calibration -->

         <com.android.settings.IconPreferenceScreen

              settings:icon="@drawable/ic_settings_privacy"

              android:title="@string/screen_calibration_settings">

              <intent 

                 android:action="android.intent.action.MAIN"

                 android:targetPackage="com.android.settings"

                 android:targetClass="com.android.settings.Calibration"/>

         </com.android.settings.IconPreferenceScreen>
 

 

         <!-- About Device -->

 

         <com.android.settings.IconPreferenceScreen

             settings:icon="@drawable/ic_settings_about"

             android:title="@string/about_settings">

             <intent

                 android:action="android.intent.action.MAIN"

                 android:targetPackage="com.android.settings"

                 android:targetClass="com.android.settings.DeviceInfoSettings" />

         </com.android.settings.IconPreferenceScreen>

Calibration.java文件代码如下

package com.android.settings;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import android.app.Activity;

import android.content.Context;

import android.graphics.Bitmap;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.os.Bundle;

import android.os.RemoteException;

import android.os.ServiceManager;

import android.util.Log;

import android.view.Display;

import android.view.IWindowManager;

import android.view.MotionEvent;

import android.view.View;

import android.view.Window;

import android.view.WindowManager;

import android.widget.Toast;

import android.os.SystemProperties;

public class Calibration extends Activity {
static final int SAMPLE_COUNTS = 5;
static final int POINT_DEGREE = 2;
static final int FACTOR_COUNTS = 7;
static final int TOP_LEFT = 0;
static final int TOP_RIGHT = 1;
static final int BOTTOM_RIGHT = 2;
static final int BOTTOM_LEFT = 3;
static final int CENTER = 4;
static final int X_AXIS = 0;
static final int Y_AXIS = 1;
static final int EDGE_GAP = 50;

static final String CALIBRATION_FILE = "/data/pointercal";
static final String TAG = "Calibration";
static final boolean DEBUG = true;

private int X_RES;
private int Y_RES;
private Display dpy;
 
class calibration {
int x[] = new int[5];
int y[] = new int[5];
int xfb[] = new int[5];
int yfb[] = new int[5];
int a[] = new int[7];
};
private calibration cal;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SystemProperties.set("sys.config.calibrate", "start");
cal = new calibration();

        
dpy = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay();

        X_RES = dpy.getWidth();

        Y_RES = dpy.getHeight();

   
this.initScreenPoints();

setContentView(new MyView(this));
}

// TopLeft-->TopRight-->BottomRight-->BottomLeft-->Center
// For 240 * 320 resolution, we use 50 pixel as edge gap
private boolean initScreenPoints() {
cal.xfb[TOP_LEFT] = EDGE_GAP;
// TopLeft
cal.yfb[TOP_LEFT] = EDGE_GAP;

cal.xfb[TOP_RIGHT] = X_RES - EDGE_GAP;
// TopRight
cal.yfb[TOP_RIGHT] = EDGE_GAP;

cal.xfb[BOTTOM_RIGHT] = X_RES - EDGE_GAP;
// BottomRight
cal.yfb[BOTTOM_RIGHT] = Y_RES - EDGE_GAP;

cal.xfb[BOTTOM_LEFT] = EDGE_GAP;
// BottomLeft
cal.yfb[BOTTOM_LEFT] = Y_RES - EDGE_GAP;

cal.xfb[CENTER] = X_RES / 2;
// Center
cal.yfb[CENTER] = Y_RES / 2;

return true;
}

private boolean perform_calibration() {
float n, x, y, x2, y2, xy, z, zx, zy;
float det, a, b, c, e, f, g;
float scaling = (float)65536.0;

n = x = y = x2 = y2 = xy = 0;
for (int i = 0; i < SAMPLE_COUNTS; i++) {
n += 1.0;
x += (float)cal.x[i];
y += (float)cal.y[i];
x2 += (float)(cal.x[i] * cal.x[i]);
y2 += (float)(cal.y[i] * cal.y[i]);
xy += (float)(cal.x[i] * cal.y[i]);
}

det = n * (x2 * y2 - xy * xy) + x * (xy * y - x * y2) + y * (x * xy - y * x2);
if (det < 0.1 && det > -0.1) {
Log.w(TAG, "determinant is too small, det =" + det);
return false;
}

if (DEBUG) {
Log.i(TAG, "(n,x,y,x2,y2,xy,det)=("
+ n + ","
+ x + ","
+ y + ","
+ x2 + ","
+ y2 + ","
+ xy + ","
+ det + ")");
}

       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;
g = (n * x2 - x * x) / det;

Log.i(TAG, "(a,b,c,e,f,g)=("
+ a + ","
+ b + ","
+ c + ","
+ e + ","
+ f + ","
+ g + ")");

// Get sums for x calibration

        z = zx = zy = 0;

        for(int i = 0; i < SAMPLE_COUNTS; i++) {

                z += (float)cal.xfb[i];

                zx += (float)(cal.xfb[i] * cal.x[i]); 

                zy += (float)(cal.xfb[i] * cal.y[i]);

        }

        // Now multiply out to get the calibration for X coordination
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 + g * zy) * (scaling));
// Get sums for y calibration

        z = zx = zy = 0;

        for(int i = 0;i < SAMPLE_COUNTS; i++) {

                z += (float)cal.yfb[i];

                zx += (float)(cal.yfb[i] * cal.x[i]);

                zy += (float)(cal.yfb[i] * cal.y[i]);

        }

        // Now multiply out to get the calibration for Y coordination
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 + g * zy) * (scaling));

cal.a[6] = (int)scaling;

return true;
}
private boolean saveCalibrationResult() {
FileOutputStream os;
String res = "";

// save the calibration factor in file system for InputDevice
try {
os = new FileOutputStream(CALIBRATION_FILE);
res = 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]);

if (DEBUG) {
Log.i(TAG, "calibration result=" + res);
}
os.write(res.getBytes());
os.close();
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
Log.w(TAG, "open calibration file write error: " + CALIBRATION_FILE );
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
}

public class MyView extends View {
private Canvas cv;
private Paint paint;
       private Bitmap bmp; 
private int screen_pos;
private Context mContext;
public MyView(Context c) {
super(c);
// set full screen and no title
requestWindowFeature(Window.FEATURE_NO_TITLE);
       
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
               WindowManager.LayoutParams.FLAG_FULLSCREEN);
       
mContext=c;
paint = new Paint();
paint.setDither(true);
paint.setAntiAlias(true);
paint.setStrokeWidth(2);
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.STROKE);
bmp = Bitmap.createBitmap(X_RES, Y_RES, Bitmap.Config.ARGB_8888);
cv = new Canvas(bmp);
screen_pos = 0;
drawCalibrationCross(screen_pos);
}

protected void onDraw(Canvas canvas) {
       canvas.drawColor(Color.BLACK);
       canvas.drawBitmap(bmp, 0, 0, null);
}

private boolean drawCalibrationCross(int pos) {

if (DEBUG) {
Log.i(TAG, "draw cross at pos " + pos);
}

cv.drawColor(Color.BLACK);

// draw X line
cv.drawLine(cal.xfb[pos] - 10, cal.yfb[pos],
cal.xfb[pos] - 2, cal.yfb[pos], paint);
cv.drawLine(cal.xfb[pos] + 2, cal.yfb[pos],
cal.xfb[pos] + 10, cal.yfb[pos], paint);

// draw Y line
cv.drawLine(cal.xfb[pos], cal.yfb[pos] - 10,
cal.xfb[pos], cal.yfb[pos] - 2, paint);
cv.drawLine(cal.xfb[pos], cal.yfb[pos] + 2,
cal.xfb[pos], cal.yfb[pos] + 10, paint);

invalidate();
return true;
}
public boolean onTouchEvent(MotionEvent event) {
float tmpx, tmpy;
boolean ret;
if (screen_pos > SAMPLE_COUNTS - 1) {
Log.i(TAG, "get sample ok");
return true;
}

if (event.getAction() == MotionEvent.ACTION_UP) {
tmpx = event.getX();
tmpy = event.getY();
if(Math.abs(cal.xfb[screen_pos]-tmpx)>15&&
Math.abs(cal.yfb[screen_pos]-tmpy)>15){
Toast.makeText(mContext, R.string.calibration_error, Toast.LENGTH_SHORT).show();
return false;
}

cal.x[screen_pos] = (int)(event.getX()*4096.0/(float)X_RES + 0.5);
cal.y[screen_pos] = (int)(event.getY()*4096.0/(float)Y_RES + 0.5);

if (screen_pos == 4) {
ret = perform_calibration();
if (ret) {
saveCalibrationResult(); 
SystemProperties.set("sys.config.calibrate", "done");
finish();
return true;
} else {
screen_pos = 0;
Log.w(TAG, "Calibration failed");
}
} else {
screen_pos++;
drawCalibrationCross(screen_pos);
}
}
return true;
}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: