再把QuickSearchBox放到桌面前,会先触发它的ConfigAcitivty,而这个config是对search 范围的选择:All, Web, Contact,Music...它的表现形式会有两种选择:一种是用ListView,另一种是用GridView.那么这里就会用到接口编程了。
我们先来看看用ListView显示的做法:Google将header+listview这样Activity用一个叫ChoiceActivty来实现,这样独立的模块,我们以后再开发的过程,若有需要也可以直接拿来用了,这样就做到了代码的重复利用了。
SearchWidgetConfigActivity就会继承ChoiceActivity:
protected CorpusView createView(ViewGroup parent) {
if (mGridView) {
return mViewFactory.createGridCorpusView(parent);
} else {
return mViewFactory.createListCorpusView(parent);
}
}这里就选择了是用GridView还是用ListView
这里就用到了接口编程的思想了:将一些规则抽象到CorpusViewFactory里面,那么通过CorpusViewFactory直接调用,不用管是如何实现的。真正的实现是在CorpusViewInflater。从代码看来若你想把ListView换成Gridview来表现,只需修改小部分代码:
1,SearchWidgetConfigActivity中:
@Override
protected void onStart() {
setAdapter(CorporaAdapter.createListAdapter(getViewFactory(), getCorpusRanker()));
super.onStart();
}
-->
@Override
protected void onStart() {
setAdapter(CorporaAdapter.createGridAdapter(getViewFactory(), getCorpusRanker()));
super.onStart();
}
2,ChoiceActivity中的choice_activity.xml
从这个例子中,可以看出了接口编程思想的重要与方便性。也许我讲得不是很清楚很明白,语言表达能力有限,只可意会不可言传。呵呵...
今天才发现,其实QuickSearchBox也用了GridView来显示search 范围的,是在CorpusSelectionDialog。
ListView的效果可以看附件里面的Listview.png,GridView看Gridview.png
我们先来看看用ListView显示的做法:Google将header+listview这样Activity用一个叫ChoiceActivty来实现,这样独立的模块,我们以后再开发的过程,若有需要也可以直接拿来用了,这样就做到了代码的重复利用了。
/*
* Copyright (C) 2009 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.quicksearchbox;
import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
/**
* Activity that shows a list of choices.
*/
public abstract class ChoiceActivity extends Activity {
protected TextView mTitleView;
protected ListView mChoicesView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.choice_activity);
mTitleView = (TextView) findViewById(R.id.alertTitle);
mChoicesView = (ListView) findViewById(R.id.list);
}
public void setHeading(int titleRes) {
mTitleView.setText(titleRes);
}
public void setHeading(CharSequence title) {
mTitleView.setText(title);
}
public void setAdapter(ListAdapter adapter) {
mChoicesView.setAdapter(adapter);
}
public void setOnItemClickListener(AdapterView.OnItemClickListener listener) {
mChoicesView.setOnItemClickListener(listener);
// TODO: for some reason, putting this in the XML layout instead makes
// the list items unclickable.
mChoicesView.setFocusable(true);
}
}
SearchWidgetConfigActivity就会继承ChoiceActivity:
/*
* Copyright (C) 2010 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.quicksearchbox;
import com.android.quicksearchbox.ui.CorporaAdapter;
import com.android.quicksearchbox.ui.CorpusViewFactory;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListAdapter;
/**
* The configuration screen for search widgets.
*/
public class SearchWidgetConfigActivity extends ChoiceActivity {
private static final boolean DBG = false;
private static final String TAG = "QSB.SearchWidgetConfigActivity";
private static final String PREFS_NAME = "SearchWidgetConfig";
private static final String WIDGET_CORPUS_NAME_PREFIX = "widget_corpus_";
private static final String WIDGET_CORPUS_SHOWING_HINT_PREFIX = "widget_showing_hint_";
private CorporaAdapter mAdapter;
private int mAppWidgetId;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setHeading(R.string.search_widget);
setOnItemClickListener(new SourceClickListener());
Intent intent = getIntent();
mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
finish();
}
// If there is only All, or only All and one other corpus, there is no
// point in asking the user to select a corpus.
if (getCorpusRanker().getRankedCorpora().size() <= 1) {
selectCorpus(null);
}
}
@Override
protected void onStart() {
setAdapter(CorporaAdapter.createListAdapter(getViewFactory(), getCorpusRanker()));
super.onStart();
}
@Override
protected void onStop() {
setAdapter(null);
super.onStop();
}
@Override
public void setAdapter(ListAdapter adapter) {
if (adapter == mAdapter) return;
if (mAdapter != null) mAdapter.close();
mAdapter = (CorporaAdapter) adapter;
super.setAdapter(adapter);
}
protected void selectCorpus(Corpus corpus) {
setWidgetCorpusName(mAppWidgetId, corpus);
SearchWidgetProvider.updateSearchWidgets(this);
Intent result = new Intent();
result.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, result);
finish();
}
private static SharedPreferences getWidgetPreferences(Context context) {
return context.getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
}
private static String getCorpusNameKey(int appWidgetId) {
return WIDGET_CORPUS_NAME_PREFIX + appWidgetId;
}
private static String getShowingHintKey(int appWidgetId) {
return WIDGET_CORPUS_SHOWING_HINT_PREFIX + appWidgetId;
}
private void setWidgetCorpusName(int appWidgetId, Corpus corpus) {
String corpusName = corpus == null ? null : corpus.getName();
SharedPreferences.Editor prefs = getWidgetPreferences(this).edit();
prefs.putString(getCorpusNameKey(appWidgetId), corpusName);
prefs.commit();
}
public static String getWidgetCorpusName(Context context, int appWidgetId) {
SharedPreferences prefs = getWidgetPreferences(context);
return prefs.getString(getCorpusNameKey(appWidgetId), null);
}
public static void setWidgetShowingHint(Context context, int appWidgetId, boolean showing) {
SharedPreferences.Editor prefs = getWidgetPreferences(context).edit();
prefs.putBoolean(getShowingHintKey(appWidgetId), showing);
boolean c = prefs.commit();
if (DBG) Log.d(TAG, "Widget " + appWidgetId + " set showing hint " + showing + "("+c+")");
}
public static boolean getWidgetShowingHint(Context context, int appWidgetId) {
SharedPreferences prefs = getWidgetPreferences(context);
boolean r = prefs.getBoolean(getShowingHintKey(appWidgetId), false);
if (DBG) Log.d(TAG, "Widget " + appWidgetId + " showing hint: " + r);
return r;
}
private CorpusRanker getCorpusRanker() {
return QsbApplication.get(this).getCorpusRanker();
}
private CorpusViewFactory getViewFactory() {
return QsbApplication.get(this).getCorpusViewFactory();
}
private class SourceClickListener implements AdapterView.OnItemClickListener {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Corpus corpus = (Corpus) parent.getItemAtPosition(position);
selectCorpus(corpus);
}
}
}
从setAdapter(CorporaAdapter.createListAdapter(getViewFactory(), getCorpusRanker()));不管是ListView还是用GrideView都会用到Adapter.
/*
* Copyright (C) 2009 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.quicksearchbox.ui;
import com.android.quicksearchbox.Corpus;
import com.android.quicksearchbox.CorpusRanker;
import android.database.DataSetObserver;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import java.util.ArrayList;
import java.util.List;
/**
* Adapter for showing a list of sources in the source selection activity.
*/
public class CorporaAdapter extends BaseAdapter {
private static final String TAG = "CorporaAdapter";
private static final boolean DBG = false;
private final CorpusViewFactory mViewFactory;
private final CorpusRanker mRanker;
private final DataSetObserver mCorporaObserver = new CorporaObserver();
private List<Corpus> mRankedEnabledCorpora;
private boolean mGridView;
private CorporaAdapter(CorpusViewFactory viewFactory,
CorpusRanker ranker, boolean gridView) {
mViewFactory = viewFactory;
mRanker = ranker;
mGridView = gridView;
mRanker.registerDataSetObserver(mCorporaObserver);
updateCorpora();
}
public static CorporaAdapter createListAdapter(CorpusViewFactory viewFactory,
CorpusRanker ranker) {
return new CorporaAdapter(viewFactory, ranker, false);
}
public static CorporaAdapter createGridAdapter(CorpusViewFactory viewFactory,
CorpusRanker ranker) {
return new CorporaAdapter(viewFactory, ranker, true);
}
private void updateCorpora() {
mRankedEnabledCorpora = new ArrayList<Corpus>();
for (Corpus corpus : mRanker.getRankedCorpora()) {
if (!corpus.isCorpusHidden()) {
mRankedEnabledCorpora.add(corpus);
}
}
notifyDataSetChanged();
}
public void close() {
mRanker.unregisterDataSetObserver(mCorporaObserver);
}
public int getCount() {
return 1 + mRankedEnabledCorpora.size();
}
public Corpus getItem(int position) {
if (position == 0) {
return null;
} else {
return mRankedEnabledCorpora.get(position - 1);
}
}
public long getItemId(int position) {
return position;
}
/**
* Gets the position of the given corpus.
*/
public int getCorpusPosition(Corpus corpus) {
if (corpus == null) {
return 0;
}
int count = getCount();
for (int i = 0; i < count; i++) {
if (corpus.equals(getItem(i))) {
return i;
}
}
Log.w(TAG, "Corpus not in adapter: " + corpus);
return 0;
}
public View getView(int position, View convertView, ViewGroup parent) {
CorpusView view = (CorpusView) convertView;
if (view == null) {
view = createView(parent);
}
Corpus corpus = getItem(position);
Drawable icon;
CharSequence label;
if (corpus == null) {
icon = mViewFactory.getGlobalSearchIcon();
label = mViewFactory.getGlobalSearchLabel();
} else {
icon = corpus.getCorpusIcon();
label = corpus.getLabel();
}
if (DBG) Log.d(TAG, "Binding " + position + ", label=" + label);
view.setIcon(icon);
view.setLabel(label);
return view;
}
protected CorpusView createView(ViewGroup parent) {
if (mGridView) {
return mViewFactory.createGridCorpusView(parent);
} else {
return mViewFactory.createListCorpusView(parent);
}
}
private class CorporaObserver extends DataSetObserver {
@Override
public void onChanged() {
updateCorpora();
}
@Override
public void onInvalidated() {
updateCorpora();
}
}
}
protected CorpusView createView(ViewGroup parent) {
if (mGridView) {
return mViewFactory.createGridCorpusView(parent);
} else {
return mViewFactory.createListCorpusView(parent);
}
}这里就选择了是用GridView还是用ListView
/*
* Copyright (C) 2009 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.quicksearchbox.ui;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.view.ViewGroup;
/**
* Creates corpus views.
*/
public interface CorpusViewFactory {
CorpusView createGridCorpusView(ViewGroup parentViewType);
CorpusView createListCorpusView(ViewGroup parentViewType);
String getGlobalSearchLabel();
Drawable getGlobalSearchIcon();
Uri getGlobalSearchIconUri();
}
/*
* Copyright (C) 2009 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.quicksearchbox.ui;
import com.android.quicksearchbox.R;
import com.android.quicksearchbox.util.Util;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.ViewGroup;
/**
* Inflates corpus views.
*/
public class CorpusViewInflater implements CorpusViewFactory {
private final Context mContext;
public CorpusViewInflater(Context context) {
mContext = context;
}
protected LayoutInflater getInflater() {
return (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public CorpusView createGridCorpusView(ViewGroup parentViewType) {
return inflateCorpusView(R.layout.corpus_grid_item, parentViewType);
}
public CorpusView createListCorpusView(ViewGroup parentViewType) {
return inflateCorpusView(R.layout.corpus_list_item, parentViewType);
}
protected CorpusView inflateCorpusView(int res, ViewGroup parentViewType) {
return (CorpusView) getInflater().inflate(res, parentViewType, false);
}
public String getGlobalSearchLabel() {
return mContext.getString(R.string.corpus_label_global);
}
private int getGlobalSearchIconResource() {
return R.drawable.search_app_icon;
}
public Drawable getGlobalSearchIcon() {
return mContext.getResources().getDrawable(getGlobalSearchIconResource());
}
public Uri getGlobalSearchIconUri() {
return Util.getResourceUri(mContext, getGlobalSearchIconResource());
}
}
/*
* Copyright (C) 2009 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.quicksearchbox.ui;
import com.android.quicksearchbox.R;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
/**
* A corpus in the corpus selection list.
*/
public class CorpusView extends RelativeLayout {
private ImageView mIcon;
private TextView mLabel;
public CorpusView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CorpusView(Context context) {
super(context);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mIcon = (ImageView) findViewById(R.id.source_icon);
mLabel = (TextView) findViewById(R.id.source_label);
}
public void setLabel(CharSequence label) {
mLabel.setText(label);
}
public void setIcon(Drawable icon) {
mIcon.setImageDrawable(icon);
}
}
这里就用到了接口编程的思想了:将一些规则抽象到CorpusViewFactory里面,那么通过CorpusViewFactory直接调用,不用管是如何实现的。真正的实现是在CorpusViewInflater。从代码看来若你想把ListView换成Gridview来表现,只需修改小部分代码:
1,SearchWidgetConfigActivity中:
@Override
protected void onStart() {
setAdapter(CorporaAdapter.createListAdapter(getViewFactory(), getCorpusRanker()));
super.onStart();
}
-->
@Override
protected void onStart() {
setAdapter(CorporaAdapter.createGridAdapter(getViewFactory(), getCorpusRanker()));
super.onStart();
}
2,ChoiceActivity中的choice_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/parentPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="9dip"
android:paddingBottom="3dip"
android:paddingLeft="3dip"
android:paddingRight="1dip"
>
<LinearLayout android:id="@+id/topPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="54dip"
android:background="@drawable/popup_top_dark"
android:orientation="vertical">
<LinearLayout android:id="@+id/titlePanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginTop="6dip"
android:layout_marginBottom="9dip"
android:layout_marginLeft="10dip"
android:layout_marginRight="10dip"
>
<ImageView android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:paddingTop="6dip"
android:paddingRight="10dip"
android:src="@drawable/ic_dialog_menu_generic" />
<TextView android:id="@+id/alertTitle"
style="?android:attr/textAppearanceLarge"
android:singleLine="false"
android:maxLines="3"
android:ellipsize="end"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
<ListView android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:cacheColorHint="@null"
android:background="@drawable/popup_bottom_bright"
android:divider="@drawable/divider_horizontal_bright"
android:scrollbars="vertical" />
</LinearLayout>
中的ListView换成GridView就可以了。
从这个例子中,可以看出了接口编程思想的重要与方便性。也许我讲得不是很清楚很明白,语言表达能力有限,只可意会不可言传。呵呵...
今天才发现,其实QuickSearchBox也用了GridView来显示search 范围的,是在CorpusSelectionDialog。
ListView的效果可以看附件里面的Listview.png,GridView看Gridview.png