[記憶] 在Android層實現觸摸屏校驗

在Android層實現觸摸屏校驗

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


 

 原文地址 http://xxw8393.blog.163.com/blog/static/3725683420098113187900/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章