API Guides(六)—— Create a Search Interface To Drag and Drop

来源:互联网 发布:淘宝最好的外贸原单店 编辑:程序博客网 时间:2024/04/25 12:32

Create a Search Interface

当用户在别的activity通过search dialog或者是widget执行一个搜索后,系统开启你的searchable activity并把搜索结果通过包含ACTION_SEARCH的Intent来传递,你的activity可以从intent的QUERY extra里面获取这个查询,然后搜索你的数据并展示结果

创建一个searchable的xml配置文件

<?xml version="1.0" encoding="utf-8"?><searchable xmlns:android="http://schemas.android.com/apk/res/android"    android:label="@string/app_label"    android:hint="@string/search_hint" ></searchable>

声明一个searchable activity

<application ... >    <activity android:name=".SearchableActivity" >        <intent-filter>            <action android:name="android.intent.action.SEARCH" />        </intent-filter>        <meta-data android:name="android.app.searchable"                   android:resource="@xml/searchable"/>    </activity>    ...</application>

执行搜索

  • 接收查询信息,用户通过search dialog或者widget执行一个搜索后,会发送一个intent到searchable activity,通过下面的方法进行接收
@Overridepublic void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.search);    // Get the intent, verify the action and get the query    Intent intent = getIntent();    if (Intent.ACTION_SEARCH.equals(intent.getAction())) {      String query = intent.getStringExtra(SearchManager.QUERY);      doMySearch(query);    }}
  • 搜索数据,可以用你的方法存储和搜索数据,对于使用SQLite数据库的,建议使用FTS3,而不是LIKE来执行全文本搜索(see sqlite.org),如果数据是存储在网上,建议使用进度条直到搜索返回结果,返回数据都建议使用Adapter封装进入列表视图进行展示
  • 展示结果

配置一个Search Widget

在3.0后,比较推荐使用widget而不是dialog的方法执行search

@Overridepublic boolean onCreateOptionsMenu(Menu menu) {    // Inflate the options menu from XML    MenuInflater inflater = getMenuInflater();    inflater.inflate(R.menu.options_menu, menu);    // Get the SearchView and set the searchable configuration    SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);    SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();    // Assumes current activity is the searchable activity    searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));    searchView.setIconifiedByDefault(false); // Do not iconify the widget; expand it by default    return true;}

search widget的其他功能

  • 一个提交按钮,默认是没有的,调用setSubmitButtonEnabled(true)提供一个
  • 完善搜索提示,可以促使用户帮你完善搜索提示,调用setQueryRefinementEnabled(true)

增加声音搜索

在searchable.xml添加下面那句代码即可

android:voiceSearchMode="showVoiceSearchButton|launchRecognizer"

launchRecognizer指定一个按钮,用来开启一个向searchable activity返回transcribed(转录)文本的识别器

Adding Recent Query Suggestions

提供最近查询建议

  • 实现一个searchable activity
  • 创建一个继承了SearchRecentSuggestionsProvider的provider并在manifest声明
  • 修改searchable配置
  • 每次搜索执行后都保存查询到provider

创建一个Provider

基本上这个类做了一切,你只需要在构造器添加一句代码即可

public class MySuggestionProvider extends SearchRecentSuggestionsProvider {    public final static String AUTHORITY = "com.example.MySuggestionProvider";    public final static int MODE = DATABASE_MODE_QUERIES; //必须有    public final static int MODE2 = DATABASE_MODE_2LINES; //可选,为每个搜索建议提供两行    public MySuggestionProvider() {        setupSuggestions(AUTHORITY, MODE);    }}
<application>    <provider android:name=".MySuggestionProvider"              android:authorities="com.example.MySuggestionProvider" />    ...</application>

修改searchable.xml配置

// 增加下面两句即可android:searchSuggestAuthority="com.example.MySuggestionProvider"android:searchSuggestSelection=" ?"

保存查询

在你的searchable activity接收到查询的时候执行保存

@Overridepublic void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.main);    Intent intent  = getIntent();    if (Intent.ACTION_SEARCH.equals(intent.getAction())) {        String query = intent.getStringExtra(SearchManager.QUERY);        SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this,                MySuggestionProvider.AUTHORITY, MySuggestionProvider.MODE);        suggestions.saveRecentQuery(query, null); // 第二个参数是在使用DATABASE_MODE_2LINES时需要    }}

清理建议数据

SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this,        HelloSuggestionProvider.AUTHORITY, HelloSuggestionProvider.MODE);suggestions.clearHistory();

Searchable Configuration

< searchable >

  • searchMode: 控制搜索表现的更多模式;”queryRewriteFromText”:使用SUGGEST_COLUNM_TEXT_1列的值来重写查询文本;”queryRewriteFromData”:使用SUGGEST_COLUMN_INTENT_DATA列的值来重写查询文本,仅用于这些值是用户能inspection(检查)和编辑的,特别是HTTP URI

下面的是提供了provider后支持的搜索建议属性

  • searchSuggestAuthority: provider的authorities
  • searchSuggestPath: 用于仅用一个provider区别多种数据类型的建议查询
  • searchSuggestSelection: 用于标识数据库的WHERE子句,一般都是”?”
  • searchSuggestIntentAction: 用户点击一个自定义搜索建议后包含在Intent里的action,默认是VIEW
  • searchSuggestIntentData: 用户点击一个自定义搜索建议后包含在Intent里的data
  • searchSuggestThreshold: 提供搜索建议的用户输入字符数的门槛,默认值是0
  • 再下面的是提供Quick Search Box的属性, 暂不太需要,就不细看了

Drag and Drop

The drag/drop process

  • Started: 为了响应用户的drag手势,应用会调用startDrag()方法告诉系统开始了一个drag. 系统首先会显示一个drag的阴影,接着系统发送一个ACTION_DRAG_STARTED的action给注册的drag事件监听器,它希望持续接收到drag事件就必须返回true,否则就无法接收到drag事件,直到系统发送ACTION_DRAG_ENDEN,这个它可以接收
  • Continuing: The user continues the drag. 监听器可能会选择改变一个视图的外观
  • Dropped: The user releases the drag shadow(就是说松手了),系统会发生ACTION_DROP给注册的监听器,The listener is expected to return boolean true to the system if code for accepting the drop succeeds.Note that this step only occurs if the user drops the drag shadow within the bounding box of a View whose listener is registered to receive drag events. If the user releases the drag shadow in any other situation, no ACTION_DROP drag event is sent.
  • Ended: 用户松手后,(在发送ACTION_DROP之后,如果发送的话)系统会发送ACTION_DRAG_ENDED到所有注册的监听器

Drag事件

系统会发出DragEvent对象的Drag事件,包含一些数据,主要包括ACTION_DRAG_STARTED, ACTION_DRAG_ENTERED, ACTION_DRAG_LOCATION, EXITED, ACTION_DROP, ACTION_DRAG_ENDED

The drag shadow

用户开始drag后,系统显示的shadow(阴影)可以通过View.DragShadowBuilder来改变,当使用参数为View的构造器创建阴影时,不需要继承它或者重写它的方法,这个View就是shadow;如果用无参的构造器时,如果不继承它或者重写它的方法,就会得到一个不可见的drag阴影,并且系统不报错;View.DragShadowBuilder有两个方法,分别是onProvideShadowMetrics()和onDrawShadow()

设计一个drag and drop的操作

开始一个drag

  • 使用ClipData和ClipData.Item来保存被移动的数据
// Sets the tagimageView.setTag(IMAGEVIEW_TAG);    ...// Sets a long click listener for the ImageView using an anonymous listener object that// implements the OnLongClickListener interfaceimageView.setOnLongClickListener(new View.OnLongClickListener() {    // Defines the one method for the interface, which is called when the View is long-clicked    public boolean onLongClick(View v) {    // Create a new ClipData.    // This is done in two steps to provide clarity. The convenience method    // ClipData.newPlainText() can create a plain text ClipData in one step.    // Create a new ClipData.Item from the ImageView object's tag    ClipData.Item item = new ClipData.Item(v.getTag());    // Create a new ClipData using the tag as a label, the plain text MIME type, and    // the already-created item. This will create a new ClipDescription object within the    // ClipData, and set its MIME type entry to "text/plain"    ClipData dragData = new ClipData(v.getTag(),ClipData.MIMETYPE_TEXT_PLAIN,item);    // Instantiates the drag shadow builder.    View.DragShadowBuilder myShadow = new MyDragShadowBuilder(imageView);    // Starts the drag            v.startDrag(dragData,  // the data to be dragged                        myShadow,  // the drag shadow builder                        null,      // no need to use local data                        0          // flags (not currently used, set to 0)            );    }}
  • 创建一个drag阴影,小的灰矩形
private static class MyDragShadowBuilder extends View.DragShadowBuilder {    // The drag shadow image, defined as a drawable thing    private static Drawable shadow;        // Defines the constructor for myDragShadowBuilder        public MyDragShadowBuilder(View v) {            // Stores the View parameter passed to myDragShadowBuilder.            super(v);            // Creates a draggable image that will fill the Canvas provided by the system.            shadow = new ColorDrawable(Color.LTGRAY);        }        // Defines a callback that sends the drag shadow dimensions and touch point back to the        // system.        @Override        public void onProvideShadowMetrics (Point size, Point touch)            // Defines local variables            private int width, height;            // Sets the width of the shadow to half the width of the original View            width = getView().getWidth() / 2;            // Sets the height of the shadow to half the height of the original View            height = getView().getHeight() / 2;            // The drag shadow is a ColorDrawable. This sets its dimensions to be the same as the            // Canvas that the system will provide. As a result, the drag shadow will fill the            // Canvas.            shadow.setBounds(0, 0, width, height);            // Sets the size parameter's width and height values. These get back to the system            // through the size parameter.            size.set(width, height);            // Sets the touch point's position to be in the middle of the drag shadow            touch.set(width / 2, height / 2);        }        // Defines a callback that draws the drag shadow in a Canvas that the system constructs        // from the dimensions passed in onProvideShadowMetrics().        @Override        public void onDrawShadow(Canvas canvas) {            // Draws the ColorDrawable in the Canvas passed in from the system.            shadow.draw(canvas);        }    }

响应一个drag的开始

在drag的开始,监听器的getAction方法会返回ACTION_DRAG_STARTED

  • 已注册的监听器调用getClipDescription来获取ClipDescription
  • 如果这监听器可以处理drop,它应该返回true,系统就会持续把drag事件发送到该监听器;否则返回false,系统就停止发送drag事件到该监听器,直到ACTION_DRAG_ENDEN
  • 在ACTION_DRAG_STARTED这个事件,下面DragEvent的方法无效:getClipData(), getX(), getY(), getResult()

在drag过程中处理事件

在drag过程中,getAction方法会返回下面三个值的其中之一
- ACTION_DRAG_ENTERED: ACTION_DRAG_ LOCATION: 需要做的处理就是改变视图的外观去表明它将接收到一个drop,The listener can also use this information to determine the exact position where the user is going to drop the drag shadow.需要做的处理就是改变视图的外观去表明它将接收到一个drop
- ACTION_DRAG_ EXITED: 需要做的就是重置视图的外观来表明这个视图不再是一个即将来临的drop目标

响应一个drop

user releases the drag shadow on a View in the application,getAction方法会返回ACTION_DROP
- 调用getClipData()获取ClipData对象并保存
- 返回true表明drop成功执行

响应一个drag的结束

Immediately after the user releases the drag shadow, the system sends a drag event to all of the drag event listeners in your application
- 如果改变了视图的默认外观,要把它重置
- 监听器可以选择性调用getResult()方法查明操作的更多信息
- 监听器应该返回true给系统

// Creates a new drag event listenermDragListen = new myDragEventListener();View imageView = new ImageView(this);// Sets the drag event listener for the ViewimageView.setOnDragListener(mDragListen);...protected class myDragEventListener implements View.OnDragListener {    // This is the method that the system calls when it dispatches a drag event to the    // listener.    public boolean onDrag(View v, DragEvent event) {        // Defines a variable to store the action type for the incoming event        final int action = event.getAction();        // Handles each of the expected events        switch(action) {            case DragEvent.ACTION_DRAG_STARTED:                // Determines if this View can accept the dragged data                if (event.getClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) {                    // As an example of what your application might do,                    // applies a blue color tint to the View to indicate that it can accept                    // data.                    v.setColorFilter(Color.BLUE);                    // Invalidate the view to force a redraw in the new tint                    v.invalidate();                    // returns true to indicate that the View can accept the dragged data.                    return true;                }                // Returns false. During the current drag and drop operation, this View will                // not receive events again until ACTION_DRAG_ENDED is sent.                return false;            case DragEvent.ACTION_DRAG_ENTERED:                // Applies a green tint to the View. Return true; the return value is ignored.                v.setColorFilter(Color.GREEN);                // Invalidate the view to force a redraw in the new tint                v.invalidate();                return true;            case DragEvent.ACTION_DRAG_LOCATION:                // Ignore the event                return true;            case DragEvent.ACTION_DRAG_EXITED:                // Re-sets the color tint to blue. Returns true; the return value is ignored.                v.setColorFilter(Color.BLUE);                // Invalidate the view to force a redraw in the new tint                v.invalidate();                return true;            case DragEvent.ACTION_DROP:                // Gets the item containing the dragged data                ClipData.Item item = event.getClipData().getItemAt(0);                // Gets the text data from the item.                dragData = item.getText();                // Displays a message containing the dragged data.                Toast.makeText(this, "Dragged data is " + dragData, Toast.LENGTH_LONG);                // Turns off any color tints                v.clearColorFilter();                // Invalidates the view to force a redraw                v.invalidate();                // Returns true. DragEvent.getResult() will return true.                return true;            case DragEvent.ACTION_DRAG_ENDED:                // Turns off any color tinting                v.clearColorFilter();                // Invalidates the view to force a redraw                v.invalidate();                // Does a getResult(), and displays what happened.                if (event.getResult()) {                    Toast.makeText(this, "The drop was handled.", Toast.LENGTH_LONG);                } else {                    Toast.makeText(this, "The drop didn't work.", Toast.LENGTH_LONG);                }                // returns true; the value is ignored.                return true;            // An unknown action type was received.            default:                Log.e("DragDrop Example","Unknown action type received by OnDragListener.");                break;        }        return false;    }};
0 0
原创粉丝点击