I wish to disable the native contextual menu that is shown when you select some text, the one with the select all, copy, share and search buttons. I do not however want to disable selections themselves. Ideally I would wish to extend the menu actually, but honestly, I am more than perfectly fine with just disabling it. With textfields and the like it tends to be relatively simple from the documentation I found, but I just can't figure out a way to make this work with XWalkView/CordovaWebView. Might be that I am just searching in entirely the wrong corner though.
I have a workaround.
For WebView there is a solution, but it doesn't work for XWalkView:
WebView selection menu workaround
My gradle includes compile 'org.xwalk:xwalk_core_library:14.43.343.17'
My solution, the main idea in the onAttachedToWindow method:
public class XWalkWebView extends XWalkView {
public XWalkWebView(Context context, AttributeSet attrs) {
super(context, attrs);
}
private ActionMode.Callback mOriginalCallback;
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
try {
View innerChild = ((ViewGroup) getChildAt(0)).getChildAt(0);
Field contentViewField = innerChild.getClass().getDeclaredField("mContentView");
contentViewField.setAccessible(true);
XWalkContentView xWalkContentView = (XWalkContentView) contentViewField.get(innerChild);
Field contentViewCoreField = xWalkContentView.getClass().getSuperclass().getDeclaredField("mContentViewCore");
contentViewCoreField.setAccessible(true);
ContentViewCore viewCore = (ContentViewCore) contentViewCoreField.get(xWalkContentView);
viewCore.setContainerView(this);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
#Override
public ActionMode startActionMode(ActionMode.Callback callback) {
mOriginalCallback = callback;
ActionMode.Callback c = new // your callback...
return super.startActionMode(c);
}
}
I try Warabei's solution but it not work on 15.44.384.13. I improve to find ContentViewCore cross versions:
public class XWalkWebView extends XWalkView {
...
private Field getFields(Class clazz){
for(Field field:clazz.getDeclaredFields()){
if(ContentViewCore.class == field.getType()){
return field;
}
}
clazz = clazz.getSuperclass();
if(clazz!=null && clazz!=Object.class){
Field field = getFields(clazz);
if(field!=null)return field;
}
return null;
}
private void inject(View view){
Field field = getFields(view.getClass());
if(field!=null){
field.setAccessible(true);
try {
ContentViewCore viewCore = (ContentViewCore) field.get(view);
viewCore.setContainerView(this);
return;
}catch(Exception e){
}
}
if(view instanceof ViewGroup){
ViewGroup viewGroup = (ViewGroup)view;
int count = viewGroup.getChildCount();
for(int i = 0;i<count;i++){
inject(viewGroup.getChildAt(i));
}
}
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
inject(this);
}
...
To disable contextual selection menu:
#Override
public ActionMode startActionMode(ActionMode.Callback callback) {
return new ActionMode() {
#Override
public void setTitle(CharSequence charSequence) {
}
#Override
public void setTitle(int i) {
}
#Override
public void setSubtitle(CharSequence charSequence) {
}
#Override
public void setSubtitle(int i) {
}
#Override
public void setCustomView(View view) {
}
#Override
public void invalidate() {
}
#Override
public void finish() {
}
#Override
public Menu getMenu() {
return null;
}
#Override
public CharSequence getTitle() {
return null;
}
#Override
public CharSequence getSubtitle() {
return null;
}
#Override
public View getCustomView() {
return null;
}
#Override
public MenuInflater getMenuInflater() {
return null;
}
};
}
It is an old post but I haven't been able to find another solution.
A simple workaround to disable context options in crosswalk view..
Go to your crosswalk project into res/menu/select_action_menu.xml
Delete or comment on the item you don't want to show
Save, build and run
This CSS should prevent context menus in both Android and IOS, as given in the cordova template
* {
-webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
}
body {
-webkit-touch-callout: none; /* prevent callout to copy image, etc when tap to hold */
-webkit-text-size-adjust: none; /* prevent webkit from resizing text to fit */
-webkit-user-select: none; /* prevent copy paste, to allow, change 'none' to 'text' */
}
Related
I'm from iOS development and new in Android app making, something looks really strange to me in Android, why EditText stay focused when keyboard is hidden ??
I've tried to set a OnFocusChangeListener on my EditText but this is not working when the keyboard hide, the listener isn't called.
I've also tried to detect keyboard hiding with a onChangeListener but it doesn't work.. (only with hard keyboard apparently).
#Override
public void onFocusChange(View v, boolean hasFocus) {
// not called when keyboard hides
}
});
I've been looking for a while and I only found answer for stopping focus at first launch but that's not what I'm looking for..
Thanks
Okay, kind of this
private void setUpEtxFocusListener() {
etx.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View view, boolean hasFocus) {
if (hasFocus) {
if ((LOG_DEBUG)) Log.d(TAG, "etx : GOT Focus");
} else {
if ((LOG_DEBUG)) Log.d(TAG, "etx : LOST Focus");
}
}
});
and when clicked outside, etx will lose focus and you hide the keyboard
//this will trigger etx setOnFocusChangeListener - onFocus change() - NO FOCUS clause
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
View v = getCurrentFocus();
if (v instanceof EditText) {
Rect outRect = new Rect();
v.getGlobalVisibleRect(outRect);
if (!outRect.contains((int) event.getRawX(), (int) event.getRawY())) {
v.clearFocus();
// just an utility class to hide.
UtilExtra.hideKeyboard(this);
}
}
}
return super.dispatchTouchEvent(event);
}
Try this:
this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
you can simply use this code and your focus will be gone
editText.clearFocus();
To catch keyboard's open/close event use this code:
//...
private int layoutSize = 0;
private boolean isKeyboardVisible = false;
//...
#Override
public void onCreate(Bundle savedInstanceState) {
setKeyboardOpenListener();
}
//...
private void setKeyboardOpenListener() {
View activityRootView = findViewById(android.R.id.content);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
if (activityRootView.getHeight() + Util.getStatusBarHeight(this) >= layoutSize) {
layoutSize = activityRootView.getHeight();
if (isKeyboardVisible) {
isKeyboardVisible = false;
onKeyboardClose();
}
} else {
if (!isKeyboardVisible) {
isKeyboardVisible = true;
onKeyboardOpen();
}
}
});
}
//...
public static int getStatusBarHeight(Context context) {
int result = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
Use editText.clearFocus() and editText.setCursorVisible(false) both methods, It may helps you.
I have implemented MVP pattern in my app. And I'm using WeakReferences to store View's reference in my Presenter. But still my fragments are not being claimed by GC upon destroying. Below is the screenshot of problem. Any idea what is causing this and how to remove this issue?
Below is the code for my Presenter:
public class ProductDetailPresenter implements ProductDetailContract.Presenter {
private final WeakReference<ProductDetailContract.View> view;
private CategoriesDataSource repo;
public ProductDetailPresenter(ProductDetailContract.View view, CategoriesDataSource repo) {
this.view = new WeakReference<>(view);
this.repo = repo;
view.setPresenter(this);
}
#Override
public void start() {
}
#Override
public void submitRating(final Product product, final float mRating) {
final ProductDetailContract.View view =ProductDetailPresenter.this.view.get();
if (view != null) {
repo.submitRating(product.getId(), mRating, true, new CategoriesDataSource.SubmitRatingCallback() {
#Override
public void onRatingSubmitted() {
product.setRating(mRating);
product.setRated(true);
product.setUpdatedAt(new Date(System.currentTimeMillis()));
repo.updateProductInDB(product);
if (!view.isActive()) return;
view.onRatingSubmitted(true, mRating);
}
#Override
public void onError(Throwable throwable) {
if (!view.isActive()) return;
view.onRatingSubmitted(false, 0);
}
});
}
}
#Override
public void onRateKarenClicked() {
ProductDetailContract.View view = this.view.get();
if (view != null) {
view.openDialog();
}
}
#Override
public void onAbhiKhareediyeClicked(Product product) {
EventBus.getDefault().post(
new ProductDetailContract.ContractEventMessages(
ProductDetailContract.ContractEventMessages.EVENT_START_QUANTITY_SCREEN, product));
}
}
This is the problem:
#Override
public void submitRating(final Product product, final float mRating) {
final ProductDetailContract.View view =ProductDetailPresenter.this.view.get(); <-- this is bad
you have a final object that is being passed to the repo. Delete the whole line. You don't need it. Use in the view.get() inside the onRatingSubmitted and onError
In a recent update of the android support library disabling the SwipeRefreshLayout also resets the layout whereas it didn't use to before.
void reset() {
mCircleView.clearAnimation();
mProgress.stop();
mCircleView.setVisibility(View.GONE);
setColorViewAlpha(MAX_ALPHA);
// Return the circle to its start position
if (mScale) {
setAnimationProgress(0 /* animation complete and view is hidden */);
} else {
setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCurrentTargetOffsetTop,
true /* requires update */);
}
mCurrentTargetOffsetTop = mCircleView.getTop();
}
#Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
if (!enabled) {
reset();
}
}
I don't want reset being called when I disable the layout, but I can't find a straightforward way of doing it. I thought of extending the SwipeRefreshLayout class and overriding setEnabled but I can't access the grandparent class' setEnabled that way. Is there a way around this?
If you place your class to android.support.v4.widget, you can do something like this:
package android.support.v4.widget;
import android.content.Context;
import android.util.AttributeSet;
/**
*
*/
public class MySwipeRefreshLayout extends SwipeRefreshLayout {
private boolean settingEnabled = false;
public MySwipeRefreshLayout(Context context) {
super(context);
}
public MySwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void setEnabled(boolean enabled) {
try {
settingEnabled = true;
super.setEnabled(enabled);
} finally {
settingEnabled = false;
}
}
#Override
void reset() {
if (!settingEnabled) {
super.reset();
}
}
}
I wrote a simple subclass of ImageView that I want to use to detect double clicks on GridView-items:
public class DoubleClickImageView extends ImageView {
public interface ClickListener {
void onSingleClick();
void onDoubleClick();
}
private ClickListener imageClickReceiver;
private GestureDetector gestureDetector;
#Override
public boolean onTouchEvent(MotionEvent event) {
gestureDetector.onTouchEvent(event);
// return super.onTouchEvent(event); does not work with gestureDetector
// return false; does not work with gestureDetector
return true; // works but breaks the rest of the application
}
public void setDoubleClickListener(ClickListener listener) {
imageClickReceiver = listener;
}
public DoubleClickImageView(Context cx, AttributeSet attrs) {
super(cx, attrs);
gestureDetector = new GestureDetector(cx, new InternalClickListener());
}
private class InternalClickListener extends GestureDetector.SimpleOnGestureListener {
#Override
public boolean onSingleTapConfirmed(MotionEvent event) {
if (imageClickReceiver != null) {
imageClickReceiver.onSingleClick();
}
return true;
}
#Override
public boolean onDoubleTap(MotionEvent e) {
if (imageClickReceiver != null) {
imageClickReceiver.onDoubleClick();
}
return true;
}
#Override
public boolean onDown(MotionEvent event) {
//return true for onDown is required according to docs but does not help
return true;
}
}
}
The GridView consists of Images that are displayed using this class.
The problem is that the double click detection works only when onTouchEvent returns true, otherwise the gestureDetector does not detect any click event.
However, when I return true in the onTouchEvent, it breaks the rest of my application, since I have a global onTouchListener to detect swipes over the whole GridView and a multiple choice select mode with long press.
How can I solve this problem so that all of these three features work together?
Update: I was able to trace down the problem with debug logs. If the initial onTouchEvent-call (MotionEvent.ACTION_DOWN) returns false,then the related follow-up-events are not delivered to the ImageView. Therefore the GestureDetector can not make any sense of it, since it needs all related MotionEvents of a given gesture.
I was finally able to find a workaround after reading the touch handling section of http://balpha.de/2013/07/android-development-what-i-wish-i-had-known-earlier/.
The workaround consists of manually passing MotionEvent's to the images of the GridView.
Therefore I extended the DoubleClickImageView class and my GridView's adapter class as follows:
/**
* A workaround to get all touch events of a gesture delivered to GridView-images,
* although the onTouchEvent-callback has to return false to prevent breaking the
* rest of the GridView-functionality. (these are swipe gestures and a contextual action bar in our case)
* See http://stackoverflow.com/questions/39107566/detect-double-click-on-imageview-works-only-when-ontouchevent-returns-true#
* http://stackoverflow.com/questions/39100565/gesturedetector-detect-doubleclick-on-gridview-items
* for more information
*/
public class DoubleClickImageView extends ImageView {
private TouchEventForwarder touchEventForwarder;
private ClickListener imageClickListener;
private GestureDetector gestureDetector;
/**
* This needs to be called in the parent GridView adapter's getView() method
**/
public void setListeners(ClickListener dest, TouchEventForwarder src) {
imageClickListener = dest;
touchEventForwarder = src;
}
/**
* The parent GridView adapter has to maintain an instance of this class
**/
public static class TouchEventForwarder {
private DoubleClickImageView currentlyClickedImage;
/**
* This needs to be called on each touch event received by the parent GridView
**/
public void forwardTouchEvent(MotionEvent event) {
if (currentlyClickedImage != null) {
currentlyClickedImage.onForwardedTouchEvent(event);
}
}
private void setNewReceiver(DoubleClickImageView doubleClickImageView) {
currentlyClickedImage = doubleClickImageView;
}
}
public interface ClickListener {
void onSingleClick();
void onDoubleClick();
}
private void onForwardedTouchEvent(MotionEvent event) {
/** Use only forwarded events for gesture detection
* to prevent the evaluation of duplicate events **/
gestureDetector.onTouchEvent(event);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
/** Use the system listener to register the view at the event forwarder**/
if (touchEventForwarder != null) {
touchEventForwarder.setNewReceiver(this);
}
return super.onTouchEvent(event); // always false
}
public DoubleClickImageView(Context cx, AttributeSet attrs) {
super(cx, attrs);
gestureDetector = new GestureDetector(cx, new InternalClickListener());
}
private class InternalClickListener extends GestureDetector.SimpleOnGestureListener {
#Override
public boolean onSingleTapConfirmed(MotionEvent event) {
if (imageClickListener != null) {
imageClickListener.onSingleClick();
}
return true;
}
#Override
public boolean onDoubleTap(MotionEvent event) {
if (imageClickListener != null) {
imageClickListener.onDoubleClick();
}
return true;
}
}
}
I've looked at many different variations of code on how to get a list of installed applications and show them in a ListView to the user but none have been successful for me. What I'd like to know is how to do this and how I'd add flags(?) to it so that I could just list curtain applications which have different intent-filters such as LONG_SEARCH_BUTTON and be able to view the package and launcher class of that application.. if this is do able?
I'd like a full java class if someone wouldn't mind sharing their knowledge as trying to piece together bits of code is becoming quite stressful! I've been coding android for a few months so I know most of the basic stuff (I'd like to think...) but not don't anything like this before.
I've written a short application which does exactly that... check it out here.
Here is the sample code from the Android documentation. Please be creative for your requirements.
/** * This class holds the per-item data in our Loader. */ public
static class AppEntry {
public AppEntry(AppListLoader loader, ApplicationInfo info) {
mLoader = loader;
mInfo = info;
mApkFile = new File(info.sourceDir);
}
public ApplicationInfo getApplicationInfo() {
return mInfo;
}
public String getLabel() {
return mLabel;
}
public Drawable getIcon() {
if (mIcon == null) {
if (mApkFile.exists()) {
mIcon = mInfo.loadIcon(mLoader.mPm);
return mIcon;
} else {
mMounted = false;
}
} else if (!mMounted) {
// If the app wasn't mounted but is now mounted, reload
// its icon.
if (mApkFile.exists()) {
mMounted = true;
mIcon = mInfo.loadIcon(mLoader.mPm);
return mIcon;
}
} else {
return mIcon;
}
return mLoader.getContext().getResources().getDrawable(
android.R.drawable.sym_def_app_icon);
}
#Override public String toString() {
return mLabel;
}
void loadLabel(Context context) {
if (mLabel == null || !mMounted) {
if (!mApkFile.exists()) {
mMounted = false;
mLabel = mInfo.packageName;
} else {
mMounted = true;
CharSequence label = mInfo.loadLabel(context.getPackageManager());
mLabel = label != null ? label.toString() : mInfo.packageName;
}
}
}
private final AppListLoader mLoader;
private final ApplicationInfo mInfo;
private final File mApkFile;
private String mLabel;
private Drawable mIcon;
private boolean mMounted; }
/** * Perform alphabetical comparison of application entry objects.
*/ public static final Comparator ALPHA_COMPARATOR = new Comparator() {
private final Collator sCollator = Collator.getInstance();
#Override
public int compare(AppEntry object1, AppEntry object2) {
return sCollator.compare(object1.getLabel(), object2.getLabel());
} };
/** * Helper for determining if the configuration has changed in an
interesting * way so we need to rebuild the app list. */ public
static class InterestingConfigChanges {
final Configuration mLastConfiguration = new Configuration();
int mLastDensity;
boolean applyNewConfig(Resources res) {
int configChanges = mLastConfiguration.updateFrom(res.getConfiguration());
boolean densityChanged = mLastDensity != res.getDisplayMetrics().densityDpi;
if (densityChanged || (configChanges&(ActivityInfo.CONFIG_LOCALE
|ActivityInfo.CONFIG_UI_MODE|ActivityInfo.CONFIG_SCREEN_LAYOUT)) != 0)
{
mLastDensity = res.getDisplayMetrics().densityDpi;
return true;
}
return false;
} }
/** * Helper class to look for interesting changes to the installed
apps * so that the loader can be updated. */ public static class
PackageIntentReceiver extends BroadcastReceiver {
final AppListLoader mLoader;
public PackageIntentReceiver(AppListLoader loader) {
mLoader = loader;
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addDataScheme("package");
mLoader.getContext().registerReceiver(this, filter);
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mLoader.getContext().registerReceiver(this, sdFilter);
}
#Override public void onReceive(Context context, Intent intent) {
// Tell the loader about the change.
mLoader.onContentChanged();
} }
/** * A custom Loader that loads all of the installed applications.
*/ public static class AppListLoader extends AsyncTaskLoader> {
final InterestingConfigChanges mLastConfig = new InterestingConfigChanges();
final PackageManager mPm;
List<AppEntry> mApps;
PackageIntentReceiver mPackageObserver;
public AppListLoader(Context context) {
super(context);
// Retrieve the package manager for later use; note we don't
// use 'context' directly but instead the save global application
// context returned by getContext().
mPm = getContext().getPackageManager();
}
/**
* This is where the bulk of our work is done. This function is
* called in a background thread and should generate a new set of
* data to be published by the loader.
*/
#Override public List<AppEntry> loadInBackground() {
// Retrieve all known applications.
List<ApplicationInfo> apps = mPm.getInstalledApplications(
PackageManager.GET_UNINSTALLED_PACKAGES |
PackageManager.GET_DISABLED_COMPONENTS);
if (apps == null) {
apps = new ArrayList<ApplicationInfo>();
}
final Context context = getContext();
// Create corresponding array of entries and load their labels.
List<AppEntry> entries = new ArrayList<AppEntry>(apps.size());
for (int i=0; i<apps.size(); i++) {
AppEntry entry = new AppEntry(this, apps.get(i));
entry.loadLabel(context);
entries.add(entry);
}
// Sort the list.
Collections.sort(entries, ALPHA_COMPARATOR);
// Done!
return entries;
}
/**
* Called when there is new data to deliver to the client. The
* super class will take care of delivering it; the implementation
* here just adds a little more logic.
*/
#Override public void deliverResult(List<AppEntry> apps) {
if (isReset()) {
// An async query came in while the loader is stopped. We
// don't need the result.
if (apps != null) {
onReleaseResources(apps);
}
}
List<AppEntry> oldApps = apps;
mApps = apps;
if (isStarted()) {
// If the Loader is currently started, we can immediately
// deliver its results.
super.deliverResult(apps);
}
// At this point we can release the resources associated with
// 'oldApps' if needed; now that the new result is delivered we
// know that it is no longer in use.
if (oldApps != null) {
onReleaseResources(oldApps);
}
}
/**
* Handles a request to start the Loader.
*/
#Override protected void onStartLoading() {
if (mApps != null) {
// If we currently have a result available, deliver it
// immediately.
deliverResult(mApps);
}
// Start watching for changes in the app data.
if (mPackageObserver == null) {
mPackageObserver = new PackageIntentReceiver(this);
}
// Has something interesting in the configuration changed since we
// last built the app list?
boolean configChange = mLastConfig.applyNewConfig(getContext().getResources());
if (takeContentChanged() || mApps == null || configChange) {
// If the data has changed since the last time it was loaded
// or is not currently available, start a load.
forceLoad();
}
}
/**
* Handles a request to stop the Loader.
*/
#Override protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
/**
* Handles a request to cancel a load.
*/
#Override public void onCanceled(List<AppEntry> apps) {
super.onCanceled(apps);
// At this point we can release the resources associated with 'apps'
// if needed.
onReleaseResources(apps);
}
/**
* Handles a request to completely reset the Loader.
*/
#Override protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
// At this point we can release the resources associated with 'apps'
// if needed.
if (mApps != null) {
onReleaseResources(mApps);
mApps = null;
}
// Stop monitoring for changes.
if (mPackageObserver != null) {
getContext().unregisterReceiver(mPackageObserver);
mPackageObserver = null;
}
}
/**
* Helper function to take care of releasing resources associated
* with an actively loaded data set.
*/
protected void onReleaseResources(List<AppEntry> apps) {
// For a simple List<> there is nothing to do. For something
// like a Cursor, we would close it here.
} }
An example implementation of a fragment that uses the above loader to show the currently installed applications in a list is below.
public static class AppListAdapter extends ArrayAdapter {
private final LayoutInflater mInflater;
public AppListAdapter(Context context) {
super(context, android.R.layout.simple_list_item_2);
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public void setData(List<AppEntry> data) {
clear();
if (data != null) {
addAll(data);
}
}
/**
* Populate new items in the list.
*/
#Override public View getView(int position, View convertView, ViewGroup parent) {
View view;
if (convertView == null) {
view = mInflater.inflate(R.layout.list_item_icon_text, parent, false);
} else {
view = convertView;
}
AppEntry item = getItem(position);
((ImageView)view.findViewById(R.id.icon)).setImageDrawable(item.getIcon());
((TextView)view.findViewById(R.id.text)).setText(item.getLabel());
return view;
} }
public static class AppListFragment extends ListFragment
implements OnQueryTextListener, LoaderManager.LoaderCallbacks> {
// This is the Adapter being used to display the list's data.
AppListAdapter mAdapter;
// If non-null, this is the current filter the user has provided.
String mCurFilter;
#Override public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Give some text to display if there is no data. In a real
// application this would come from a resource.
setEmptyText("No applications");
// We have a menu item to show in action bar.
setHasOptionsMenu(true);
// Create an empty adapter we will use to display the loaded data.
mAdapter = new AppListAdapter(getActivity());
setListAdapter(mAdapter);
// Start out with a progress indicator.
setListShown(false);
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);
}
#Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// Place an action bar item for searching.
MenuItem item = menu.add("Search");
item.setIcon(android.R.drawable.ic_menu_search);
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM
| MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
SearchView sv = new SearchView(getActivity());
sv.setOnQueryTextListener(this);
item.setActionView(sv);
}
#Override public boolean onQueryTextChange(String newText) {
// Called when the action bar search text has changed. Since this
// is a simple array adapter, we can just have it do the filtering.
mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
mAdapter.getFilter().filter(mCurFilter);
return true;
}
#Override public boolean onQueryTextSubmit(String query) {
// Don't care about this.
return true;
}
#Override public void onListItemClick(ListView l, View v, int position, long id) {
// Insert desired behavior here.
Log.i("LoaderCustom", "Item clicked: " + id);
}
#Override public Loader<List<AppEntry>> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
// sample only has one Loader with no arguments, so it is simple.
return new AppListLoader(getActivity());
}
#Override public void onLoadFinished(Loader<List<AppEntry>> loader, List<AppEntry> data) {
// Set the new data in the adapter.
mAdapter.setData(data);
// The list should now be shown.
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
#Override public void onLoaderReset(Loader<List<AppEntry>> loader) {
// Clear the data in the adapter.
mAdapter.setData(null);
} }