• 【Android】SDK NoteEditor源码研究(一、组件重载)


      1 package com.jercy.android.SDKNotePad;
      2 
      3 import android.app.Activity;
      4 import android.content.ComponentName;
      5 import android.content.ContentValues;
      6 import android.content.Context;
      7 import android.content.Intent;
      8 import android.database.Cursor;
      9 import android.graphics.Canvas;
     10 import android.graphics.Paint;
     11 import android.graphics.Rect;
     12 import android.net.Uri;
     13 import android.os.Bundle;
     14 import android.util.AttributeSet;
     15 import android.util.Log;
     16 import android.view.Menu;
     17 import android.view.MenuItem;
     18 import android.widget.EditText;
     19 
     20 import com.jercy.android.SDKNotePad.NotePad.Notes;
     21 
     22 public class NoteEditor extends Activity {
     23 
     24     private static final String TAG = "NoteEditor";
     25     
     26     private static final String[] PROJECTION = new String[]{
     27          Notes._ID, // 0
     28          Notes.NOTE, // 1
     29     };
     30     private static final int COLUMN_INDEX_NOTE = 1;
     31     
     32     //用来保存便签信息,以便在退出或者失去控制时能够及时更新到数据库中
     33     private static final String ORIGINAL_CONTENT = "origContent";
     34     
     35     private static final int REVERT_ID = Menu.FIRST;
     36     private static final int DISCARD_ID = Menu.FIRST + 1;
     37     private static final int DELETE_ID = Menu.FIRST + 2;
     38     
     39     //用来区分两种操作状态
     40     private static final int STATE_EDIT = 0;
     41     private static final int STATE_INSERT = 1;
     42     
     43     private int mState;
     44     private boolean mNoteOnly = false;
     45     private Uri mUri;
     46     private Cursor mCursor;
     47     private EditText mText;
     48     private String mOriginalContent;
     49     
     50     /**
     51      * 一个定制的EditText每行信息都会用下滑线显示.
     52      */
     53     public static class LinedEditText extends EditText{
     54         private Rect mRect;
     55         private Paint mPaint;
     56         public LinedEditText(Context context, AttributeSet attrs) {
     57             super(context, attrs);
     58             
     59             mRect = new Rect();
     60             mPaint = new Paint();
     61             mPaint.setStyle(Paint.Style.STROKE);
     62             mPaint.setColor(0x800000FF);
     63         }
     64         @Override
     65         protected void onDraw(Canvas canvas) {
     66             int count = getLineCount();
     67             Rect r = mRect;
     68             Paint paint = mPaint;
     69 
     70             for (int i = 0; i < count; i++) {
     71                 int baseline = getLineBounds(i, r);
     72 
     73                 canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);
     74             }
     75             super.onDraw(canvas);
     76         }
     77     }
     78     
     79     @Override
     80     protected void onCreate(Bundle savedInstanceState) {
     81         super.onCreate(savedInstanceState);
     82         Log.i(TAG, "Enter in NoteEditor的onCreate方法");
     83         final Intent intent = getIntent();
     84 
     85         // Do some setup based on the action being performed.
     86         final String action = intent.getAction();
     87         if (Intent.ACTION_EDIT.equals(action)) {
     88             // Requested to edit: set that state, and the data being edited.
     89             mState = STATE_EDIT;
     90             mUri = intent.getData();
     91         } else if (Intent.ACTION_INSERT.equals(action)) {
     92             // Requested to insert: set that state, and create a new entry
     93             // in the container.
     94             mState = STATE_INSERT;
     95             Log.i(TAG, "getContentResolver().insert(intent.getData(), null),其中intent.getData().toString()为:"+intent.getData().toString());
     96             mUri = getContentResolver().insert(intent.getData(), null);
     97 
     98             // If we were unable to create a new note, then just finish
     99             // this activity.  A RESULT_CANCELED will be sent back to the
    100             // original activity if they requested a result.
    101             if (mUri == null) {
    102                 Log.e(TAG, "Failed to insert new note into " + getIntent().getData());
    103                 finish();
    104                 return;
    105             }
    106 
    107             // The new entry was created, so assume all will end well and
    108             // set the result to be returned.
    109             Log.i(TAG, "返回RESULT_OK,setAction(mUri.toString(),其中mUri.toString()为:"+mUri.toString());
    110             setResult(RESULT_OK, (new Intent()).setAction(mUri.toString()));
    111 
    112         } else {
    113             // Whoops, unknown action!  Bail.
    114             Log.e(TAG, "Unknown action, exiting");
    115             finish();
    116             return;
    117         }
    118 
    119         // Set the layout for this activity.  You can find it in res/layout/note_editor.xml
    120         setContentView(R.layout.note_editor);
    121         
    122         // The text view for our note, identified by its ID in the XML file.
    123         mText = (EditText) findViewById(R.id.note);
    124 
    125         // Get the note!
    126         mCursor = managedQuery(mUri, PROJECTION, null, null, null);
    127 
    128         // If an instance of this activity had previously stopped, we can
    129         // get the original text it started with.
    130         if (savedInstanceState != null) {
    131             mOriginalContent = savedInstanceState.getString(ORIGINAL_CONTENT);
    132         }
    133     }
    134 
    135     @Override
    136     protected void onResume() {
    137         super.onResume();
    138         Log.i(TAG, "Enter in NoteEditor的onResume方法");
    139         // If we didn't have any trouble retrieving the data, it is now time to get at the stuff.
    140         if (mCursor != null) {
    141             // Make sure we are at the one and only row in the cursor.
    142             mCursor.moveToFirst();
    143 
    144             // Modify our overall title depending on the mode we are running in.
    145             if (mState == STATE_EDIT) {
    146                 setTitle(getText(R.string.title_edit));
    147             } else if (mState == STATE_INSERT) {
    148                 setTitle(getText(R.string.title_create));
    149             }
    150 
    151             // This is a little tricky: we may be resumed after previously being
    152             // paused/stopped.  We want to put the new text in the text view,
    153             // but leave the user where they were (retain the cursor position
    154             // etc).  This version of setText does that for us.
    155             String note = mCursor.getString(COLUMN_INDEX_NOTE);//显示title的数据列的信息
    156             mText.setTextKeepState(note);
    157             
    158             // If we hadn't previously retrieved the original text, do so
    159             // now.  This allows the user to revert their changes.      
    160             if (mOriginalContent == null) {
    161                 mOriginalContent = note;
    162             }
    163             Log.i(TAG, "Enter in NoteEditor的onResume方法后,当前mOriginalContent的值为:"+mOriginalContent);
    164         } else {
    165             setTitle(getText(R.string.error_title));
    166             mText.setText(getText(R.string.error_message));
    167         }
    168     }
    169 
    170     @Override
    171     /**
    172      * 运用onPause()和onSaveInstanceState保存数据 ,对这两个方法的使用进行讲解
    173      * 参考:http://dev.10086.cn/cmdn/wiki/index.php?edition-view-6259-1.html
    174      * 本例在测试中调用该方法的情景为:在编辑Note时,直接按home键回到首页,就先执行onSaveInstanceState方法,然后执行onPause方法。
    175      * 再次打开程序时会直接进入刚刚编辑note的NoteEditor界面,因为在离开程序时,底层Activity.class会执行该方法,保持当前程序退出的状态:
    176      * final void performSaveInstanceState(Bundle outState) {
    177      *  onSaveInstanceState(outState);
    178      *  saveManagedDialogs(outState);
    179      * }
    180      */
    181     protected void onSaveInstanceState(Bundle outState) {
    182         // Save away the original text, so we still have it if the activity
    183         // needs to be killed while paused.
    184         //界面销毁之前保存数据
    185         Log.i(TAG, "Enter in NoteEditor的onSaveInstanceState方法后,当前mOriginalContent的值为:"+mOriginalContent);
    186         outState.putString(ORIGINAL_CONTENT, mOriginalContent);
    187     }
    188 
    189     @Override
    190     protected void onPause() {
    191         super.onPause();
    192       //界面失去控制权时保存数据
    193         Log.i(TAG, "Enter in NoteEditor的onPause方法");
    194         // The user is going somewhere else, so make sure their current
    195         // changes are safely saved away in the provider.  We don't need
    196         // to do this if only editing.
    197         if (mCursor != null) {
    198             String text = mText.getText().toString();
    199             int length = text.length();
    200 
    201             // If this activity is finished, and there is no text, then we
    202             // do something a little special: simply delete the note entry.
    203             // Note that we do this both for editing and inserting...  it
    204             // would be reasonable to only do it when inserting.
    205             if (isFinishing() && (length == 0) && !mNoteOnly) {
    206                 setResult(RESULT_CANCELED);
    207                 deleteNote();
    208 
    209             // Get out updates into the provider.
    210             } else {
    211                 ContentValues values = new ContentValues();
    212 
    213                 // This stuff is only done when working with a full-fledged note.
    214                 if (!mNoteOnly) {
    215                     // Bump the modification time to now.
    216                     values.put(Notes.MODIFIED_DATE, System.currentTimeMillis());
    217 
    218                     // If we are creating a new note, then we want to also create
    219                     // an initial title for it.
    220                     if (mState == STATE_INSERT) {
    221                         String title = text.substring(0, Math.min(30, length));
    222                         if (length > 30) {
    223                             int lastSpace = title.lastIndexOf(' ');
    224                             if (lastSpace > 0) {
    225                                 title = title.substring(0, lastSpace);
    226                             }
    227                         }
    228                         values.put(Notes.TITLE, title);
    229                     }
    230                 }
    231 
    232                 // Write our text back into the provider.
    233                 values.put(Notes.NOTE, text);
    234 
    235                 // Commit all of our changes to persistent storage. When the update completes
    236                 // the content provider will notify the cursor of the change, which will
    237                 // cause the UI to be updated.
    238                 getContentResolver().update(mUri, values, null, null);
    239             }
    240         }
    241     }
    242 
    243     @Override
    244     public boolean onCreateOptionsMenu(Menu menu) {
    245         super.onCreateOptionsMenu(menu);
    246         Log.i(TAG, "Enter in NoteEditor的onCreateOptionsMenu方法");
    247         // Build the menus that are shown when editing.
    248         if (mState == STATE_EDIT) {
    249             menu.add(0, REVERT_ID, 0, R.string.menu_revert)
    250                     .setShortcut('0', 'r')
    251                     .setIcon(android.R.drawable.ic_menu_revert);
    252             if (!mNoteOnly) {
    253                 menu.add(0, DELETE_ID, 0, R.string.menu_delete)
    254                         .setShortcut('1', 'd')
    255                         .setIcon(android.R.drawable.ic_menu_delete);
    256             }
    257 
    258         // Build the menus that are shown when inserting.
    259         } else {
    260             menu.add(0, DISCARD_ID, 0, R.string.menu_discard)
    261                     .setShortcut('0', 'd')
    262                     .setIcon(android.R.drawable.ic_menu_delete);
    263         }
    264 
    265         // If we are working on a full note, then append to the
    266         // menu items for any other activities that can do stuff with it
    267         // as well.  This does a query on the system for any activities that
    268         // implement the ALTERNATIVE_ACTION for our data, adding a menu item
    269         //for each one that is found.
    270         //这里使用动态的方法出创建菜单项
    271         if (!mNoteOnly) {
    272             Intent intent = new Intent(null, getIntent().getData());
    273             intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
    274             menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0,
    275                     new ComponentName(this, NoteEditor.class), null, intent, 0, null);
    276         }
    277         /*    注意这里就会产生一个问题,效果中menuItem的菜单项如何显示,即如何找到我们对于string里的常量的,
    278             经过测试后,发现它会默认先找:
    279              <intent-filter android:label="@string/resolve_title">
    280             这个label标签,找不到的话,就会往上找,
    281             <activity android:name="TitleEditor" android:label="@string/title_edit_title"
    282             就会找到这个activity的label显示
    283          */
    284         
    285         return true;
    286     }
    287 
    288     @Override
    289     public boolean onOptionsItemSelected(MenuItem item) {
    290         Log.i(TAG, "Enter in NoteEditor的onOptionsItemSelected方法");
    291         // Handle all of the possible menu actions.
    292         switch (item.getItemId()) {
    293         case DELETE_ID:
    294             deleteNote();
    295             finish();
    296             break;
    297         case DISCARD_ID:
    298             cancelNote();
    299             break;
    300         case REVERT_ID:
    301             cancelNote();
    302             break;
    303         }
    304         return super.onOptionsItemSelected(item);
    305     }
    306 
    307     /**
    308      * Take care of canceling work on a note.  Deletes the note if we
    309      * had created it, otherwise reverts to the original text.
    310      */
    311     private final void cancelNote() {
    312         Log.i(TAG, "Enter in NoteEditor的cancelNote方法");
    313         if (mCursor != null) {
    314             if (mState == STATE_EDIT) {
    315                 // Put the original note text back into the database
    316                 mCursor.close();
    317                 mCursor = null;
    318                 ContentValues values = new ContentValues();
    319                 values.put(Notes.NOTE, mOriginalContent);
    320                 getContentResolver().update(mUri, values, null, null);
    321             } else if (mState == STATE_INSERT) {
    322                 // We inserted an empty note, make sure to delete it
    323                 deleteNote();
    324             }
    325         }
    326         setResult(RESULT_CANCELED);
    327         finish();
    328     }
    329 
    330     /**
    331      * Take care of deleting a note.  Simply deletes the entry.
    332      */
    333     private final void deleteNote() {
    334         Log.i(TAG, "Enter in NoteEditor的deleteNote方法");
    335         if (mCursor != null) {
    336             mCursor.close();
    337             mCursor = null;
    338             getContentResolver().delete(mUri, null, null);
    339             mText.setText("");
    340         }
    341     }
    342 }

    这个类的最大的特点就是重载了组件EditText!效果如下:

    每行字都加了下划线,解释一下实现原理(注:这篇文章主要讲解这里重载)

    主要的方法就是:

     1 @Override
     2         protected void onDraw(Canvas canvas) {
     3             int count = getLineCount();
     4             Rect r = mRect;
     5             Paint paint = mPaint;
     6 
     7             for (int i = 0; i < count; i++) {
     8                 int baseline = getLineBounds(i, r);
     9                 canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);
    10             }
    11             super.onDraw(canvas);
    12         }

    这里调用了两个EditText自带的两个方法,一个是getLineCount():获取总的行数,一个是getLineBounds():传入的参数第一个是表示第几行,第二个参数是第N行所占的Rect,然后以此为标准进行画线。

    根据我的猜测:

    1)因为是继承的EditText,所以会在Focus状态下自动调出键盘;

    2)每次字符变换都会自动调用onDraw方法重绘界面,这种情况下,不论你是换行还是删除字符,都能保证写到哪一行就画线画到哪一行,也就会形成上面的那种效果。

  • 相关阅读:
    数组和类集排序总结
    toString()方法
    冒泡排序java
    Scanner类输入字符串和整形数字
    身份证分组
    jsonp的实质
    在伪数组上部署iterator方法
    获取属性的方法
    合并以及对于引用克隆
    深度复制对象已解决循环引用
  • 原文地址:https://www.cnblogs.com/lqminn/p/2805695.html
Copyright © 2020-2023  润新知