2013년 1월 24일 목요일

[Android] InputMethodManager를 이용한 텍스트 입력..

Android InputMethodManager 이용한 Text 입력

* Android에서 텍스트를 입력하는 방법에 대해 테스트를 해본다.
* 2가지 방법을 이용하여 sample project를 만들어 본다.
  1. EditText (TextWatcher) 이용
  2. InputConnection (KeyListener) 이용

< EditText 이용>

1. 화면에 TextView와 EditText를 하나 구성한다.

        /*
         * Step 1
         *  - Backgroud로 보여줄 이미지를 설정한다.
         *  - Text가 보여질 TextView를 추가한다.
         *  - TextView에 입력하기 위해 EditText를 하나 생성한다.
         *  
         *  - EditText를 터치하면, IME가 자동으로 올라온다(show).
         *  - IME를 내릴경우(hide) 취소 키를 눌러야 하고, EditText에는 커서가 그대로 남아있다.
         */
        // main layout
        ViewGroup.LayoutParams layoutParamsMain = 
          new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 
                     ViewGroup.LayoutParams.MATCH_PARENT); 
        
        // sub layout
        ViewGroup.LayoutParams layoutParamsSub =
          new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                     ViewGroup.LayoutParams.WRAP_CONTENT);
        
        FrameLayout mainLayout = new FrameLayout(this);
        
        LinearLayout subLayout = new LinearLayout(this);
        subLayout.setOrientation(LinearLayout.VERTICAL);
        
        // Background 용 ImageView
        mImageView = new ImageView(this);
        mImageView.setBackgroundResource(R.drawable.iu3);

        // Text 입력을 위한 EditText와 Text를 보여줄 TextView
        mTextView = new TextView(this);
        mEditText = new EditText(this);
     
        // Initial Text 설정.
        mTextView.setText(mInitText1);  
        mEditText.setText(mInitText2);
        
        // Add Views ( ImageView, TextView )
        mainLayout.addView(mImageView, layoutParamsMain);
        subLayout.addView(mTextView, layoutParamsSub);
        subLayout.addView(mEditText, layoutParamsSub);
        
        mainLayout.addView(subLayout, layoutParamsSub);
2. EditText에 TextWatcher의 Listener를 붙인다.
  - 3개의 method를 이용한다(onTextChanged, beforeTextChanged, afterTextChanged).
        /*
         * Step 2
         *  - EditText 에 Text가 입력될 때마다 변경되는 Text를 TextView에 보여주기 위해 
         *  - TextWathcer를 implements 한다.
         *  - 또는 아래 주석처럼 사용할 수도 있겠다.
         *  
         *  - Step 2까지 하게되면, EditText 에 Text를 입력하면 TextView에도 그 Text가 반영된다.
         *  - 이 상태 역시 EditText를 터치하면, IME가 자동으로 올라오게 된다(show).
         *  - IME를 내릴경우(hide) 취소 키를 눌러야 하고, EditText에는 커서가 그대로 남아있다.
         */
        mEditText.addTextChangedListener(this);
        
//        mTextWatcher = new TextWatcher() {
//   
//   @Override
//   public void onTextChanged(CharSequence s, int start, int before, int count) {
//    // TODO Auto-generated method stub
//    Log.i(TAG , "onTextChanged() is called");
//    Log.e(TAG, "String = " + s);
//    
//    mTextView.setText(s);
//   }
//   
//   @Override
//   public void beforeTextChanged(CharSequence s, int start, int count, int after) {
//    // TODO Auto-generated method stub
//    Log.i(TAG , "beforeTextChanged() is called");
//    Log.e(TAG, "String = " + s);
//   }
//   
//   @Override
//   public void afterTextChanged(Editable s) {
//    // TODO Auto-generated method stub
//    Log.i(TAG , "afterTextChanged() is called");
//    Log.e(TAG, "String = " + s);
//   }
//  };
//  mEditText.addTextChangedListener(mTextWatcher);

    /*
     * Step 2
     *  - EditText 에 Text가 입력될 때마다 변경되는 Text를 보기위해 TextWathcer를 implements 한다.
     *  - 아래 method들을 이용하면 되겠다.
     */
    // TextWatcher methods
 @Override
 public void afterTextChanged(Editable s) {
  // TODO Auto-generated method stub
 }

 @Override
 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
  // TODO Auto-generated method stub
 }

 @Override
 public void onTextChanged(CharSequence s, int start, int before, int count) {
  // TODO Auto-generated method stub
  Log.i(TAG , "onTextChanged() is called");
  Log.e(TAG, "String = " + s);
  
  // TextView에 입력된 Text를 반영하자.
  mTextView.setText(s);
 }

3. TouchListener를 이용하여 IME를 보여주자.
 * 이 단계는 생략하는 것이 EditText를 사용하는데 더 좋은 것 같다.
        /*
         * Step 3
         *  - 이제 TouchListener를 이용하여 IME의 show를 control 해보자.
         *  - 사길 Step 2까지만 하는 것이 EditText에 텍스트를 입력하는 것에는 더 좋을 것이다.
         *  - Step 2에서는 cursor의 위치 이동, 텍스트 잘라내기, 붙여넣기 등의 기능들이 자동으로 제공된다.
         *  - 오히려, EditText를 터치하는 동작에서 IME를 보여주게 되면 기존의 기능들을 별도로 해줘야 한다.
         *  - cursor 위치 이동, 복사, 잘라내기, 붙여넣기 등...
         *  - IME를 보이게 하는 것을 테스트하기 위함이라 아래 코드를 추가한다.
         */
        mEditText.setOnTouchListener(this);  


 /*
  * Step 3
  *  - Touch 동작에 맞게 IME를 보여주는 것을 해보자(show).
  *  - InputMethodManager의 INPUT_METHOD_SERVICE를 이용한다.
  */
 @Override
 public boolean onTouch(View view, MotionEvent event) {
  // TODO Auto-generated method stub
  Log.i(TAG , "onTouch() is called");
  
  switch (event.getAction()) {
  case MotionEvent.ACTION_DOWN:
   break;
   
  case MotionEvent.ACTION_MOVE:
   break;
   
  case MotionEvent.ACTION_UP:
   // Touch Up 동작에서 IME를 보여주자.
   showIME(view);
   
   break;
  }
  
  return true;
 }
 
 /**
  * IME를 보이게 해준다.
  * @param view : target view
  */
 public void showIME(View view) {
  Log.i(TAG , "showIME() is called");
  CharSequence initText = null;
  
  initText = mTextView.getText();
  mEditText.setText(initText); // TextView의 초기 Text를 EditText로 보이게 하자.
  
  InputMethodManager mInputMethod = (InputMethodManager) this.getSystemService(Context.INPUT_METHOD_SERVICE);
  Log.e(TAG , "mInputMethod  = " + mInputMethod);
  
  mInputMethod.showSoftInput(view, InputMethodManager.SHOW_FORCED);
 }


< InputConnection 이용 >

1. 화면에 TextView를 하나 구성한다.
  - 위의 1단계의 코드와 별반 다를 것은 없고, EditText만 삭제하면 된다.

2. TextView에 TouchListener를 달아서 테스트를 해보자.
  - InputConnection을 이용한다.

        /*
         * Step 2
         *  - 이제 TouchListener를 이용하여 IME의 show를 control 해보자.
         *  - 텍스트를 보여 줄 TextView를 터치하면, IME 가 나오도록 한다.
         */  
        mTextView.setOnTouchListener(this);
        
        /*
         * Step 3
         *  - IME로부터 입력되는 Text를 보여주기 위한 것.
         *  - TextView에 연결된 IME로부터 Key 입력을 받기 위해, KeyListener를 연결한다.
         */
        mTextView.setFocusable(true);
        mTextView.setFocusableInTouchMode(true); // 이게 있어야 TextView안의 Text가 변경된다.
        mTextView.requestFocus();
        mTextView.focusSearch(View.FOCUS_DOWN);
        
        mKeyListener = TextKeyListener.getInstance();
        mTextView.setKeyListener(mKeyListener);

  - onTouch()에서 ACTION_UP 이벤트에 동작하게 하자.
 /*
  * Step 2
  *  - TextView를 터치하였을 때, IME를 올리도록 하자(show).
  *  - IME와 연동을 위해 InputConnection을 생성하자.
  */
 @Override
 public boolean onTouch(View view, MotionEvent event) {
  // TODO Auto-generated method stub
  Log.i(TAG , "onTouch() is called");
  
  switch (event.getAction()) {
  case MotionEvent.ACTION_DOWN:
   break;
   
  case MotionEvent.ACTION_MOVE:
   break;
   
  case MotionEvent.ACTION_UP:
   // Touch Up 동작에서 IME를 보여주자.
   showIME(view);
   
   break;
  }
  
  return true;
 }
 
 /**
  * IME를 보이게 해준다.
  * @param view : target view
  */
 public void showIME(View view) {
  Log.i(TAG , "showIME() is called");
  
  // Text 정보를 전달하기 위해 InputConnection을 생성한다.
  mEditorInfo = new EditorInfo();
  onCreateInputConnection(mEditorInfo);
  
  InputMethodManager mInputMethod = (InputMethodManager) this.getSystemService(Context.INPUT_METHOD_SERVICE);
  Log.e(TAG , "mInputMethod  = " + mInputMethod);

  // IME를 올려보자.
  mInputMethod.showSoftInput(view, InputMethodManager.SHOW_FORCED);
  // 아래와 같이 하여도 동작한다.
//  mInputMethod.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);
  
 }

  - InputConnection과 연결을 한다.

 /*
  * 텍스트 입력을 위해 InputConnection과의 연결을 한다.
  * 
  * @param outAttrs : Editor의 속성 값들을 전달한다.
  * @return InputConnection의 Instance.
  */
 public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
  Log.i(TAG , "onCreateInputConnection() is called");
  Log.i(TAG , "onCreateInputConnection() mInputConnection = " + mInputConnection);
  
  if(mInputConnection == null ) {
   mInputConnection = new EditableInputConnection(mTextView, true);
  }
  
  return mInputConnection;
  
 }

* 위에서 테스트한 코드들은 텍스트를 입력하여, TextView에 보이게만 한 것이다.
* EditText를 이용할 때, 복사, 붙여넣기, 잘라내기, Cursor 이동에 대해서는 테스트가 더 필요해 보인다.
* 테스트할 것들이 많구만...

< Source >

https://bitbucket.org/snoh/androidinputmethodtest

Mercurial command : hg clone https://bitbucket.org/snoh/androidinputmethodtest


< 끝 >

2013년 1월 23일 수요일

[Android] Hierarchy Viewer를 사용 하려면..

* Android app.의 layout 구조가 어떻게 되는지 궁금하여, HierarchyViewer를 이용해 보려고 한다.

* hierarchyviewer.bat 를 실행시키는 순간... 아래와 같은 에러 등장...

[hierarchyviewer]Unable to get view server protocol version from device 
[hierarchyviewer]Unable to debug device

음... 뭘까.. 전엔 잘 되었던 것 같은데..

이래 저래 찾다가 아래 사이트를 발견..

Reference (Thanks to Julia)..

Enable HierarchyViewer on production builds


http://jmlinnik.blogspot.ro/2012/08/enable-hierarchyviewer-on-production.html

음.. 그렇구나...

단말이 production build가 된 경우에는 동작을 안한다.

userdebug build나 engineering build가 된 경우에만 동작을 한다.
(예전에 잘되었던 것은 userdebug로 빌드를 한 것 같다).

위의 link에서처럼 RomainGuy의 ViewServer 소스를 받아서 해본다.

소스는 git hub 에 올려져 있음.
https://github.com/romainguy/ViewServer

App. 소스에서 3군데에 추가해 주면 끝나는군...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MyActivity extends Activity {
    public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         // Set content view, etc.
         ViewServer.get(this).addWindow(this);
     }
 
     public void onDestroy() {
         super.onDestroy();
         ViewServer.get(this).removeWindow(this);
    }
 
     public void onResume() {
         super.onResume();
         ViewServer.get(this).setFocusedWindow(this);
    }
}


아.. 그리고,
INTERNET permission을 추가해 줘야 한다..

1
2
<uses-permission android:name="android.permission.INTERNET">
</uses-permission>



블로그에 올려준 Julia에게 감사하고, 소스를 올려준 RomainGuy에게도 고마움을 느낀다..

소소한 것이라 생각했다가 막상 찾으려니 도움이 되어 좋다...