I am trying to do a simple widget for my app, which displays data from an API on the widget using ListView, but the list is empty even tho Logs says that the data was downloaded successfully.
Blank listView is displayed with no data in it. No errors are shown. What's the cause of it?
Resources I used when tried to solve this problem:
official documentation - https://developer.android.com/guide/topics/appwidgets#collections
similar question - How to use listview in a widget?
my AppWidgetProvider
public class Widget extends AppWidgetProvider {
RemoteViews views;
void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
CharSequence widgetText = context.getString(R.string.appwidget_text);
// Construct the RemoteViews object
views = new RemoteViews(context.getPackageName(), R.layout.my_widget);
views.setTextViewText(R.id.appwidget_text, widgetText);
// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views);
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// There may be multiple widgets active, so update all of them
for (int appWidgetId : appWidgetIds) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.my_widget);
Intent intent = new Intent(context, WidgetService.class);
remoteViews.setRemoteAdapter(R.id.appwidget_list_view, intent);
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
}
}
WidgetFactory and WidgetService
public class WidgetFactory implements RemoteViewsService.RemoteViewsFactory{
final Context context;
List<SimpleCoin> list;
List<Coin> coinList;
public WidgetFactory(Context context, Intent intent) {
this.context = context;
}
#Override
public void onCreate() {
//get data from database
CoinDatabase coinDatabase = CoinDatabase.buildDatabase(context);
list = coinDatabase.coinDao().getAllSimpleCoinsNonLive();
coinList = new ArrayList<>();
}
#Override
public void onDataSetChanged() {
//based on data from database download content (JSON)
if(list != null) {
Log.d("Widget", "Coin size -> " + list.size());
StringBuilder id = new StringBuilder("id=" + list.get(0).getId());
for (int j = 0; j < list.size(); j++) {
id.append(",");
id.append(list.get(j).getId());
}
String finalUrl = URL + id.toString() + API_KEY;
Observable.fromCallable(() -> {
HttpURLConnection connection = null;
BufferedReader reader = null;
java.net.URL url1 = new URL(finalUrl);
connection = (HttpURLConnection) url1.openConnection();
connection.connect();
InputStream stream = connection.getInputStream();
reader = new BufferedReader(new InputStreamReader(stream));
StringBuilder buffer = new StringBuilder();
String line = "";
while ((line = reader.readLine()) != null) {
buffer.append(line).append("\n");
}
connection.disconnect();
reader.close();
return buffer.toString();
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<String>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
}
#Override
public void onNext(#NonNull String s) {
if (s != null) {
try {
//Create new coin and update list of coins
JSONObject object = new JSONObject(s);
for (int i = 0; i < list.size(); i++) {
JSONObject jsonObject = object.getJSONObject("data").getJSONObject(String.valueOf(list.get(i).getId()));
Log.d("Widget", "Coin -> " + jsonObject.getString("name") + jsonObject.getJSONObject("quote").getJSONObject("USD").getDouble("price") + jsonObject.getJSONObject("quote").getJSONObject("USD").getDouble("percent_change_7d"));
Coin coin = new Coin(jsonObject.getString("name"), jsonObject.getJSONObject("quote").getJSONObject("USD").getDouble("price"), jsonObject.getJSONObject("quote").getJSONObject("USD").getDouble("percent_change_7d"), false);
coinList.add(coin);
getViewAt(i);
}
} catch (JSONException e) {
Log.d("Widget", "Coin -> " + e.toString());
e.printStackTrace();
}
}
}
#Override
public void onError(#NonNull Throwable e) {
}
#Override
public void onComplete() {
}
});
}
}
#Override
public void onDestroy() {}
#Override
public int getCount() {
return list.size();
}
#Override
public RemoteViews getViewAt(int i) {
//Set ListData based on coinList
Log.d("Widget", "Coin size getViewAt -> " + list.size() + "item num ->" + i);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.my_widget_list_item);
if(coinList != null)
if(coinList.size() > i) {
Log.d("Widget", "Coin position added" + coinList.get(i).getName());
remoteViews.setTextViewText(R.id.widgetItemTaskNameLabel, coinList.get(i).getName());
}
return remoteViews;
}
#Override
public RemoteViews getLoadingView() {
return null;
}
#Override
public int getViewTypeCount() {
return 1;
}
#Override
public long getItemId(int i) {
return i;
}
#Override
public boolean hasStableIds() {
return true;
}
}
public class WidgetService extends RemoteViewsService {
#Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
return new WidgetFactory(this.getApplicationContext(), intent);
}
}
Here are my logs:
D/Widget: Coin size getViewAt -> 1 item num ->0
D/Widget: Coin size getViewAt -> 1 item num ->0
D/Widget: Coin -> testCoin0.00.0
D/Widget: Coin size getViewAt -> 1 item num ->0
D/Widget: Coin position added testCoin
my_widget.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:background="#color/colorPrimary"
android:padding="#dimen/widget_margin">
<TextView
android:id="#+id/appwidget_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_margin="8dp"
android:background="#color/colorPrimary"
android:contentDescription="#string/appwidget_text"
android:text="#string/appwidget_text"
android:textColor="#ffffff"
android:textSize="24sp"
android:textStyle="bold|italic" />
<ListView
android:id="#+id/appwidget_list_view"
android:layout_width="match_parent"
android:layout_margin="8dp"
tools:listitem="#layout/my_widget_list_item"
android:layout_height="wrap_content"
android:background="#color/colorAccent"
android:layout_below="#id/appwidget_text">
</ListView>
</RelativeLayout>
my_widget_list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:paddingLeft="#dimen/widget_listview_padding_x"
android:paddingRight="#dimen/widget_listview_padding_x"
android:paddingStart="#dimen/widget_listview_padding_x"
android:paddingEnd="#dimen/widget_listview_padding_x"
android:minHeight="#dimen/widget_listview_item_height"
android:weightSum="2"
android:layout_height="wrap_content">
<TextView
android:id="#+id/widgetItemTaskNameLabel"
android:layout_width="0dp"
android:gravity="start"
android:layout_weight="1"
android:textColor="#color/colorPrimary"
android:layout_gravity="center_vertical"
android:layout_height="wrap_content" />
</LinearLayout>
I found my mistake, turns out my API call shouldn't be asynchronous because as the documentation says:
public void onDataSetChanged() ->
You can do heaving lifting in here, synchronously. For example, if you need to process an image, fetch something from the network, etc., it is ok to do it here, synchronously. The widget will remain in its current state while work is being done here, so you don't need to worry about locking up the widget.
So getting my data from Asynchronous call was the reason why data wasn't displayed
Related
I have made an Image Editor and it works fine. Now, in my recent edits page, I added a feature select multiple items. But, I get a strange behaviour when I refresh the recycler view. See this ->
https://www.veed.io/view/43fa65a4-24e6-44e8-8cea-468ca8c62d7a.
This is my adapter code ->
public class RecentsAdapter extends RecyclerView.Adapter<RecentsAdapter.ViewHolder>{
private List<ImageFile> files;
private final PopupListener popupListener;
RecentItemListeners recentItemListeners;
public Integer selectedItemsCount = 0;
TextView neEditsTV;
public RecentsAdapter(List<ImageFile> files, PopupListener popupListener, RecentItemListeners listeners, TextView neEditsTV) {
this.files = files;
this.popupListener = popupListener;
this.recentItemListeners = listeners;
this.neEditsTV = neEditsTV;
}
public void updateList(List<ImageFile> filesList){
if (files.size() < filesList.size()){
int dif = filesList.size() - files.size();
this.files = filesList;
for (int i = 0; i < dif; i++) {
notifyItemInserted(i);
}
}else if (files.size() > filesList.size()){
for (int i = 0; i < filesList.size(); i++) {
notifyItemChanged(i);
}
notifyDataSetChanged();
notify();
}else {
this.files = filesList;
for (int i = 0; i < filesList.size(); i++) {
notifyItemChanged(i);
}
}
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new ViewHolder(ItemRecentBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
}
#SuppressLint("NewApi")
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
holder.setData(files.get(position));
}
#Override
public int getItemCount() {
return files.size();
}
public class ViewHolder extends RecyclerView.ViewHolder implements TinyDBManager.ValueChangeListener {
ItemRecentBinding binding;
boolean mIsSelected = false;
public ViewHolder(#NonNull ItemRecentBinding itemView) {
super(itemView.getRoot());
binding = itemView;
}
#SuppressLint({"ClickableViewAccessibility", "ResourceAsColor"})
#RequiresApi(api = Build.VERSION_CODES.Q)
public void setData(ImageFile file){
TinyDBManager tinyDBManager = TinyDB.getInstance(itemView.getContext());
tinyDBManager.setValueChangeListener(this);
binding.fileName.setText(file.getName());
binding.preview.setImageURI(Uri.parse(file.getPath()));
Log.d("positionChecker",getAdapterPosition() + "");
#SuppressLint("SimpleDateFormat") String datept1 = new SimpleDateFormat("dd/MM/yyyy").format(file.getLastModified());
#SuppressLint("SimpleDateFormat") String datept3 = new SimpleDateFormat("hh:mm:ss a").format(file.getLastModified());
binding.lastEdited.setText(datept1 + " at " + datept3);
binding.option.setOnClickListener(v -> {
ContextThemeWrapper contextWrapper = new ContextThemeWrapper(itemView.getContext(),R.style.Theme_ImageEditor);
PopupMenu menu = new PopupMenu(contextWrapper, binding.option);
menu.inflate(R.menu.recent_item_menu);
menu.setForceShowIcon(true);
menu.show();
menu.setOnDismissListener(menu1 -> popupListener.onPopupClosed());
popupListener.onPopupOpened();
menu.setOnMenuItemClickListener(item -> {
switch (item.getTitle().toString()){
case "Open":
Intent openIntent = new Intent();
openIntent.setAction(Intent.ACTION_VIEW);
openIntent.setDataAndType(Uri.parse(file.getPath()), "image/*");
openIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
itemView.getContext().startActivity(openIntent);
break;
case "Share":
Intent shareIntent = new Intent(Intent.ACTION_SEND);
Uri screenshotUri = Uri.parse(file.getPath());
shareIntent.putExtra(Intent.EXTRA_STREAM, screenshotUri);
shareIntent.setType("image/*");
itemView.getContext().startActivity(Intent.createChooser(shareIntent, "Share image via..."));
break;
case "Edit":
Intent editIntent = new Intent(itemView.getContext(), ImageEditorActivity.class);
editIntent.putExtra("filePath",file.getPath());
editIntent.putExtra("position",getAdapterPosition());
Toast.makeText(contextWrapper, "" + file.getName(), Toast.LENGTH_SHORT).show();
editIntent.putExtra("name",file.getName());
editIntent.putExtra("new",false);
v.getContext().startActivity(editIntent);
break;
case "Remove from list":
TinyDBManager tinyDB = TinyDB.getInstance(v.getContext());
List<ImageFile> imageFiles = tinyDB.get("files",null);
imageFiles.remove(getAdapterPosition());
files.remove(getAdapterPosition());
tinyDB.put("files",imageFiles);
notifyItemRemoved(getAdapterPosition());
break;
case "Delete":
CustomAlertDialog dialog = new CustomAlertDialog(itemView.getContext(),null);
View.OnClickListener[] clickListeners = new View.OnClickListener[2];
clickListeners[0] = v1 -> dialog.dismiss();
clickListeners[1] = v1 -> {
dialog.dismiss();
String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + file.getName();
boolean deleteFile = new File(path).delete();
Log.d("path:",file.getPath());
if (deleteFile)
Toast.makeText(contextWrapper, "success", Toast.LENGTH_SHORT).show();
else Toast.makeText(contextWrapper, "fail", Toast.LENGTH_SHORT).show();
MediaScannerConnection.scanFile(itemView.getContext(), new String[] { Environment.getExternalStorageDirectory().toString() }, null, (path1, uri) -> {
Log.i("ExternalStorage", "Scanned " + path1 + ":");
Log.i("ExternalStorage", "-> uri=" + uri);
});
};
dialog.show(itemView.getContext(), "Delete image","Are you sure you want to delete the image?\nThis action cannot be reverted.", new String[]{"No","Yes"},clickListeners);
}
return true;
});
});
binding.share.setOnClickListener(v -> {
Intent shareIntent = new Intent(Intent.ACTION_SEND);
Uri screenshotUri = Uri.parse(file.getPath());
shareIntent.putExtra(Intent.EXTRA_STREAM, screenshotUri);
shareIntent.setType("image/*");
itemView.getContext().startActivity(Intent.createChooser(shareIntent, "Share image via..."));
});
binding.getRoot().setOnClickListener(v -> {
if (selectedItemsCount != 0) {
if (mIsSelected) {
mIsSelected = false;
selectedItemsCount--;
binding.getRoot().setCardBackgroundColor(Color.WHITE);
binding.lastEdited.setTextColor(Color.GRAY);
binding.fileName.setTextColor(Color.BLACK);
binding.option.setImageTintList(ColorStateList.valueOf(Color.GRAY));
recentItemListeners.onItemClick(getAdapterPosition(), 1, file.getPath(),file);
} else {
recentItemListeners.onItemClick(getAdapterPosition(), 0, file.getPath(),file);
binding.getRoot().setCardBackgroundColor(itemView.getContext().getColor(R.color.primary));
binding.lastEdited.setTextColor(Color.WHITE);
binding.fileName.setTextColor(Color.WHITE);
binding.option.setImageTintList(ColorStateList.valueOf(Color.WHITE));
mIsSelected = true;
selectedItemsCount++;
}
}else {
mIsSelected = false;
binding.getRoot().setCardBackgroundColor(Color.WHITE);
binding.lastEdited.setTextColor(Color.GRAY);
binding.fileName.setTextColor(Color.BLACK);
binding.option.setImageTintList(ColorStateList.valueOf(Color.GRAY));
}
});
binding.getRoot().setOnLongClickListener(v -> {
mIsSelected = true;
binding.getRoot().setCardBackgroundColor(itemView.getContext().getColor(R.color.primary));
binding.lastEdited.setTextColor(Color.WHITE);
binding.fileName.setTextColor(Color.WHITE);
binding.option.setImageTintList(ColorStateList.valueOf(Color.WHITE));
recentItemListeners.onItemLongClick(getAdapterPosition(), file.getPath(), file);
selectedItemsCount++;
return true;
});
}
#Override
public <E> void onValueAdded(String key, E value) {
files = (List<ImageFile>) value;
files.sort(Comparator.comparing(obj -> obj.getLastModified()));
Collections.reverse(files);
notifyDataSetChanged();
neEditsTV.setVisibility(files.size() == 0 ? View.VISIBLE : View.GONE);
}
#Override
public void onKeyRemoved(String key) {
}
#Override
public void onAllKeysRemoved() {
}
}
}
And now my activity interface listener which are passed to the adapter ->
#Override
public void onItemClick(int position, int action, String filePath, ImageFile file) {
if (selectedItems.contains(file) && action == 1) {
selectedItemsCount--;
filePaths.remove(position);
selectedItems.remove(selectedItems.get(position));
if (selectedItemsCount == 0){
binding.toolbar.setTitle(getString(R.string.app_name));
binding.toolbar.getMenu().clear();
binding.toolbar.setNavigationIcon(null);
canUpdate = true;
}else {
binding.toolbar.setTitle("" + selectedItemsCount);
binding.toolbar.getMenu().clear();
binding.toolbar.inflateMenu(R.menu.multi_select_options);
binding.toolbar.setNavigationIcon(R.drawable.left);
canUpdate = false;
}
}else {
selectedItemsCount++;
selectedItems.add(file);
filePaths.add(filePath);
binding.toolbar.setTitle(selectedItemsCount + " Selected");
binding.toolbar.getMenu().clear();
binding.toolbar.inflateMenu(R.menu.multi_select_options);
binding.toolbar.setNavigationIcon(R.drawable.left);
canUpdate = false;
}
}
#Override
public void onItemLongClick(int position, String filePath, ImageFile file) {
if (selectedItems.contains(file)) return;
selectedItemsCount++;
selectedItems.add(file);
filePaths.add(filePath);
binding.toolbar.setTitle(selectedItemsCount + " Selected");
binding.toolbar.getMenu().clear();
binding.toolbar.inflateMenu(R.menu.multi_select_options);
binding.toolbar.setNavigationIcon(R.drawable.left);
canUpdate = false;
}
And my item file:
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="#dimen/_65sdp"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
app:cardCornerRadius="#dimen/_6sdp"
android:padding="#dimen/_10sdp"
app:cardElevation="2dp"
android:layout_marginTop="#dimen/_12sdp"
android:layout_marginHorizontal="#dimen/_6sdp"
android:background="?selectableItemBackground">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:background="?selectableItemBackground">
<ImageView
android:id="#+id/preview"
android:layout_width="#dimen/_70sdp"
android:layout_height="match_parent"
tools:src="#tools:sample/backgrounds/scenic"
android:scaleType="centerCrop"/>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical"
android:layout_marginStart="#dimen/_10sdp"
android:paddingVertical="#dimen/_2sdp"
android:gravity="center">
<TextView
android:id="#+id/fileName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sample file.png"
style="#style/TextAppearance.AppCompat.Title"
android:gravity="center_vertical"
android:ellipsize="end"
android:maxLines="1"
android:layout_weight="1"/>
<TextView
android:id="#+id/lastEdited"
style="#style/TextAppearance.AppCompat.Subhead"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="1"
android:text="Sample file.png"
android:textColor="#android:color/darker_gray"
android:layout_weight="1"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="#+id/option"
android:layout_width="#dimen/_24sdp"
android:layout_height="0dp"
android:src="#drawable/more_vert"
android:padding="#dimen/_2sdp"
android:background="?selectableItemBackgroundBorderless"
app:tint="#android:color/darker_gray"
android:layout_weight="1"/>
<ImageView
android:id="#+id/share"
android:layout_width="#dimen/_24sdp"
android:layout_height="0dp"
android:src="#drawable/share"
android:padding="#dimen/_2sdp"
android:background="?selectableItemBackgroundBorderless"
app:tint="#android:color/holo_red_light"
android:layout_weight="1"/>
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
How can I prevent this behaviour?
fragment_picture.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="#+id/picture_fragment"
android:layout_height="match_parent"
android:background="#F1F1F1">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="#+id/swipeRefresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rcv_picture"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:spanCount="1"
android:visibility="gone"
tools:listitem="#layout/item_picture" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<androidx.constraintlayout.widget.Group
android:id="#+id/group_no_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="visible"
app:constraint_referenced_ids="imv_no_data,tv_no_data" />
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/imv_no_data"
android:layout_width="#dimen/_100sdp"
android:layout_height="#dimen/_100sdp"
android:layout_marginBottom="#dimen/_12sdp"
android:padding="#dimen/_8sdp"
android:src="#drawable/ic_no_image"
app:layout_constraintBottom_toTopOf="#id/tv_no_data"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tv_no_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="#dimen/_16sdp"
android:text="#string/no_image"
android:textColor="#color/color_828282"
android:textSize="#dimen/_14sdp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ProgressBar
android:visibility="gone"
android:id="#+id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/_16sdp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/tv_no_data" />
</androidx.constraintlayout.widget.ConstraintLayout>
FragmentPicture.java
public class PictureFragment extends BaseFragment<FragmentPictureBinding> {
private static final int REQUEST_PERMISSION_DELETE = 376;
private PictureAdapter pictureAdapter;
private int pos;
SwipeRefreshLayout swipeRefresh;
#Override
protected void initView() {
pictureAdapter = new PictureAdapter(new ArrayList<>(), getContext());
pictureAdapter.setCallBackAdapter(item -> openMedia(item.getPath()));
pictureAdapter.setCallBackPicture(new PictureAdapter.CallBackPicture() {
#Override
public void onClickMore(String s, int adapterPosition, View v) {
showPopupMenuMore(s, adapterPosition, v);
}
});
binding.rcvPicture.setAdapter(pictureAdapter);
binding.swipeRefresh.setOnRefreshListener(() -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
getData();
}else {
if (getActivity() instanceof MainActivity){
((MainActivity) getActivity()).askPermissionStorageMain();
}
}
} else {
getData();
}
binding.swipeRefresh.setRefreshing(false);
});
binding.swipeRefresh.setColorSchemeResources(R.color.color_accent,
android.R.color.holo_green_dark,
android.R.color.holo_orange_dark,
android.R.color.holo_blue_dark);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
getData();
}
} else {
getData();
}
}
public void onCLickMore(String item, int pos, View view) {
showPopupMenuMore(item, pos, view);
}
private void showPopupMenuMore(String item, int pos, View view) {
PopupMenu popupMenu = new PopupMenu(getContext(), view);
popupMenu.inflate(R.menu.popup_menu_more_picture);
popupMenu.show();
// popupMenu.getMenu().getItem(1).setEnabled(PreferencesHelper.getBoolean(PreferencesHelper.KEY_SAVE_AS_GIF, true));
popupMenu.setOnMenuItemClickListener(menuItem ->{
switch (menuItem.getItemId()) {
case R.id.share_picture:
shareMedia(item);
break;
case R.id.delete_picture:
this.pos = pos;
deletePhoto(item);
break;
}
return true;
});
}
private void getData() {
getAllFilesInPicture()
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(this::mapFiles)
.subscribe(datas -> {
if (datas.isEmpty()) {
binding.rcvPicture.setVisibility(View.GONE);
binding.groupNoData.setVisibility(View.VISIBLE);
} else {
binding.rcvPicture.setVisibility(View.VISIBLE);
binding.groupNoData.setVisibility(View.GONE);
pictureAdapter.addDatas(datas);
}
},throwable -> {
});
}
public Single<File[]> getAllFilesInPicture() {
return Single.create(sub -> {
File[] listFile = Storage.getFilesImageInStorage(requireContext());
if (listFile != null) {
sub.onSuccess(listFile);
} else {
sub.onSuccess(new File[]{});
}
});
}
public Single<List<VideoFile>> mapFiles(#NonNull File[] listFile) {
return Single.create(sub -> {
long previousDate = 0;
List<VideoFile> videoFiles = new ArrayList<>();
Arrays.sort(listFile, (f1, f2) -> Long.compare(f1.lastModified(), f2.lastModified()));
List<String> pictureFile = new ArrayList<>();
for (int i = listFile.length - 1; i >= 0; i--) {
VideoFile videoFile = new VideoFile();
if (!Toolbox.isSameDay(previousDate, listFile[i].lastModified())) {
VideoFile header = new VideoFile();
header.setHeader(true);
header.setLastModified(listFile[i].lastModified());
videoFiles.add(header);
}
videoFile.setPath(listFile[i].getAbsolutePath());
videoFile.setName(listFile[i].getName());
videoFile.setSize(listFile[i].length());
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(new File(listFile[i].getPath()).getAbsolutePath(), options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
videoFile.setResolution(imageWidth+"x"+imageHeight);
videoFile.setSize(listFile[i].length());
videoFile.setLastModified(listFile[i].lastModified());
videoFiles.add(videoFile);
previousDate = listFile[i].lastModified();
pictureFile.add(listFile[i].getAbsolutePath());
}
sub.onSuccess(videoFiles);
});
}
#Override
protected void initControl() {
}
private void itemToItem(String filepath){
}
private void openMedia(String filePath) {
Uri fileUri = FileProvider.getUriForFile(
getContext(), getContext().getPackageName() + ".provider",
new File(filePath));
try {
Intent openVideoIntent = new Intent();
openVideoIntent.setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_ACTIVITY_NEW_TASK)
.setDataAndType(
fileUri,
getContext().getContentResolver().getType(fileUri));
getContext().startActivity(openVideoIntent);
} catch (Exception e) {
e.printStackTrace();
}
}
private void shareMedia(String filePath) {
Uri fileUri = FileProvider.getUriForFile(
getContext(), getContext().getPackageName() + ".provider",
new File(filePath));
Intent shareIntent = new Intent()
.setAction(Intent.ACTION_SEND)
.putExtra(Intent.EXTRA_STREAM, fileUri)
.setType(filePath.endsWith(".mp4") ? "video/mp4" : "image/*");
shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getContext().startActivity(shareIntent);
}
protected void deletePhoto(String path) {
AlertDialog.Builder alert = new AlertDialog.Builder(getActivity());
alert.setTitle("Delete");
alert.setMessage("Do you want to delete this image ?");
alert.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
//This will delete my Image from recycleView
deleteMedia(path);
//I Wan't to add here code for refreshing recycle view
}
});
alert.setNegativeButton("No", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
alert.show();
}
private void deleteMedia(String filePath) {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
new File(filePath).delete();
//removeItem(pos);
MediaScannerConnection.scanFile(getActivity(),
new String[]{filePath}, new String[]{"video/mp4"},
(path1, uri) -> {
});
} else {
MediaScannerConnection.scanFile(getActivity(),
new String[]{filePath}, new String[]{"video/mp4"},
(path1, uri) -> {
if (uri != null) {
try {
if (getActivity().getContentResolver().delete(uri, null, null) != -1) {
//removeItem(pos);
}
swipeRefresh.setRefreshing(false);
} catch (SecurityException e) {
List<Uri> uris = new ArrayList<>();
uris.add(uri);
requestDeletePermission(uris);
}
}
});
}
}
private void requestDeletePermission(List<Uri> uriList) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
try {
PendingIntent pi = MediaStore.createDeleteRequest(getActivity().getContentResolver(), uriList);
startIntentSenderForResult(pi.getIntentSender(), REQUEST_PERMISSION_DELETE, null, 0, 0,
0, null);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
}
}
private void removeItem(int pos) {
getBaseActivity().runOnUiThread(() -> {
pictureAdapter.getList().remove(pos);
pictureAdapter.notifyItemRemoved(pos);
if (pictureAdapter.getList().get(pictureAdapter.getList().size() - 1).isHeader()) {
pictureAdapter.getList().remove(pictureAdapter.getList().size() - 1);
pictureAdapter.notifyItemRemoved(pictureAdapter.getList().size() - 1);
}
if (pictureAdapter.getList().isEmpty()) {
binding.rcvPicture.setVisibility(View.GONE);
binding.groupNoData.setVisibility(View.VISIBLE);
binding.progress.setVisibility(View.GONE);
} else {
binding.rcvPicture.setVisibility(View.VISIBLE);
binding.groupNoData.setVisibility(View.GONE);
}
//Have?
});
}
#Override
protected boolean isNeedRefresh() {
return true;
}
#Override
protected FragmentPictureBinding getViewBinding(LayoutInflater inflater, ViewGroup container) {
return FragmentPictureBinding.inflate(LayoutInflater.from(getContext()));
}
#Override
public void onReceivedEvent(RxBusType type, Object data) {
switch (type) {
case SCREEN_SHOT:
case NOTI_MEDIA_CHANGE:
getData();
break;
}
}
}
I added popup menu on picture_item of recycle view and when we press delete button of popup menu I send String path of that image to deletePhoto(item); and that method delete image.
after delete image we have to manually refresh fragment by pull refresh But can add any automatic method for refreshing fragment In this case?
Please see deletePhoto method where I deleted photo but when
I tried refresh with removepos method its crash
I tried also many methods from stackoverflow but did not worked on my case
Please help me
You have already done everything uncomment your removeItem method and add pictureAdapter.notifyDataSetChanged() at the end of removeItem method
This will reload the data set of your adapter
If you know the position of the deleted element you can use
pictureAdapter.notifyItemRemoved(position)
This will tell your adapter you remove an item from the data set on a specific position
I have an app that utilizes a TSL 1128 reader to read RFID tags. It uses the IASCIICommandResponder Library to communicate with the reader.
I have one Class called orderFullfill.java that is responsible for the Listview and updating the UI for this class. Th
The Reader functionality is set up in another class (orderResponder.java) that implements this IASCIICommandResponder, so that it can run in the background whilst orderFullfill shows the changes made to the ArrayList.
I have tried passing the Arrayadapter from orderFullfill.java to orderResponder to call notifyDataSetChanged(); but that has no effect. I have to call addPigNumber() method on a button click to update my listview.
Ideally I need the listview to update when a new tag is added to the list. Please can someone help me apply notifyDataSetChanged() in the right place so that when I can a tag it will add to the list automatically. I am very grateful for any help that is received.
public class orderFullfill extends AppCompatActivity implements PopupMenu.OnMenuItemClickListener {int where; public static ArrayList scannedPigs = new ArrayList<String>();
// The Reader currently in use
private Reader mReader = null;
private boolean mIsSelectingReader = false;
// The model that performs actions with the reader
private TriggerModel mModel;
public static String dispatchweek;
public static String customer;
public static String accountnumber;
public static String supplyfarm;
public static String breed;
public static int numberofpigs;
public static int pigNum;
public static ArrayAdapter<String> adapter;
public static String weekNow;
public static Context mContext;
Switch trick; TextView nameholder;
/**
* #return the current AsciiCommander
*/
protected AsciiCommander getCommander()
{
return AsciiCommander.sharedInstance();
}
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.orderfullfill);
mContext = orderFullfill.this;
addPigNumber();
InventoryCommand iCommand = InventoryCommand.synchronousCommand();
iCommand.setFilterStrongest(TriState.YES);//high power reading
trick = (Switch) findViewById(R.id.scanM2);
trick.setVisibility(View.INVISIBLE);
trick.setChecked(true);
trick.setText(getResources().getString(R.string.lowPowerScan));
trick.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
InventoryCommand iCommand = new InventoryCommand();
//InventoryCommand iCommand = InventoryCommand.synchronousCommand();
if(isChecked)
{
iCommand.setTakeNoAction(TriState.YES);
iCommand.setFilterStrongest(TriState.YES);// low power reading
getCommander().executeCommand(iCommand);
trick.setText(getResources().getString(R.string.lowPowerScan));
}
if(!isChecked)
{
trick.setText(getResources().getString(R.string.highPowerScan));
iCommand.setFilterStrongest(TriState.NO);//high power reading
iCommand.setTakeNoAction(TriState.NO);
getCommander().executeCommand(iCommand);
}
}
});
nameholder = (TextView)findViewById(R.id.nameView);
nameholder.setText(customer);
AsciiCommander.createSharedInstance(getApplicationContext());
final AsciiCommander commander = getCommander();
// Ensure that all existing responders are removed
commander.clearResponders();
// Add the LoggerResponder - this simply echoes all lines received from the reader to the log
// and passes the line onto the next responder
// This is ADDED FIRST so that no other responder can consume received lines before they are logged.
// commander.addResponder(new LoggerResponder());// logcat input
// Add responder to enable the synchronous commands
// commander.addSynchronousResponder();
commander.addResponder(new orderResponder());
// Configure the ReaderManager when necessary
ReaderManager.create(getApplicationContext());
// Add observers for changes
ReaderManager.sharedInstance().getReaderList().readerAddedEvent().addObserver(mAddedObserver);
ReaderManager.sharedInstance().getReaderList().readerUpdatedEvent().addObserver(mUpdatedObserver);
ReaderManager.sharedInstance().getReaderList().readerRemovedEvent().addObserver(mRemovedObserver);
// Create a (custom) model and configure its commander and handler
mModel = new TriggerModel();
mModel.setCommander(getCommander());
mModel.initialise();
ListView scroller = (ListView)findViewById(R.id.listviewD);
scroller.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
PopupMenu popup = new PopupMenu(orderFullfill.this, view);
popup.setOnMenuItemClickListener(orderFullfill.this);
popup.inflate(R.menu.edit_menu);
where = position;
popup.show();
}
});
weekNow = orderResponder.getWeeknow();
}
public ArrayAdapter getAdapter()
{
adapter = new ArrayAdapter<>(mContext, android.R.layout.simple_spinner_item, scannedPigs);
return adapter;
}
public void addPigNumber()
{
adapter = new ArrayAdapter<String>(mContext, android.R.layout.simple_spinner_item, scannedPigs);
TextView counter = findViewById(R.id.editTextNumber);
if(numberofpigs >= 0 )
{
counter.setText("Pigs Left: " +numberofpigs);
if(numberofpigs == 0)
{
counter.setBackgroundColor(Color.parseColor("#BCBDF3E8"));
}
}
ListView scroller = findViewById(R.id.listviewD);
try {
adapter.setDropDownViewResource(R.layout.support_simple_spinner_dropdown_item);
scroller.setAdapter(adapter);
}catch (Exception e)
{
String result ="";
result.equals(e.toString());
}
}
public void orderdetailsOnclick(View view) {
addPigNumber();
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setTitle("Customer Order: " + customer);
alert.setMessage("Account Number: " + accountnumber + "\r\n" +
"Dispatch Week: " + dispatchweek + "\r\n" +
"Supply Farm: " + supplyfarm + "\r\n" +
"Breed: "+ breed + "\r\n" + "Number of Pigs: " + pigNum);
alert.setCancelable(true);
alert.show();
}
#Override
public synchronized void onResume()
{
super.onResume();
addPigNumber();
// Register to receive notifications from the AsciiCommander
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, new IntentFilter(AsciiCommander.STATE_CHANGED_NOTIFICATION));
// Remember if the pause/resume was caused by ReaderManager - this will be cleared when ReaderManager.onResume() is called
boolean readerManagerDidCauseOnPause = ReaderManager.sharedInstance().didCauseOnPause();
// The ReaderManager needs to know about Activity lifecycle changes
ReaderManager.sharedInstance().onResume();
// The Activity may start with a reader already connected (perhaps by another App)
// Update the ReaderList which will add any unknown reader, firing events appropriately
ReaderManager.sharedInstance().updateList();
// Locate a Reader to use when necessary
AutoSelectReader(!readerManagerDidCauseOnPause);
mIsSelectingReader = false;
displayReaderState();
}
#Override
public synchronized void onPause() {
super.onPause();
// Register to receive notifications from the AsciiCommander
LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
addPigNumber();
ReaderManager.sharedInstance().onPause();
}
#Override
protected void onDestroy()
{
super.onDestroy();
// Remove observers for changes
ReaderManager.sharedInstance().getReaderList().readerAddedEvent().removeObserver(mAddedObserver);
ReaderManager.sharedInstance().getReaderList().readerUpdatedEvent().removeObserver(mUpdatedObserver);
ReaderManager.sharedInstance().getReaderList().readerRemovedEvent().removeObserver(mRemovedObserver);
// readResponder.tempList = false;
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.reader_menu, menu);
// Reset, connect, disconnect dropdown menu
mResetMenuItem = menu.findItem(R.id.reset_reader_menu_item);
mConnectMenuItem = menu.findItem(R.id.connect_reader_menu_item);
mDisconnectMenuItem= menu.findItem(R.id.disconnect_reader_menu_item);
return true;
}
//----------------------------------------------------------------------------------------------
// ReaderList Observers
//----------------------------------------------------------------------------------------------
Observable.Observer<Reader> mAddedObserver = new Observable.Observer<Reader>()
{
#Override
public void update(Observable<? extends Reader> observable, Reader reader)
{
// See if this newly added Reader should be used
AutoSelectReader(true);
}
};
Observable.Observer<Reader> mUpdatedObserver = new Observable.Observer<Reader>()
{
#Override
public void update(Observable<? extends Reader> observable, Reader reader)
{
}
};
Observable.Observer<Reader> mRemovedObserver = new Observable.Observer<Reader>()
{
#Override
public void update(Observable<? extends Reader> observable, Reader reader)
{
// Was the current Reader removed
if( reader == mReader)
{
mReader = null;
// Stop using the old Reader
getCommander().setReader(mReader);
}
}
};
#Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId())
{
case R.id.delete_menu:
boolean complete = false;
scannedPigs.remove(where);
numberofpigs = numberofpigs + 1;
//myDB.deleteEntry(scannedItems); -> IF YOU ADD IT TO THE DB REMOVE IT
//select items from the list
addPigNumber();
return true;
case R.id.edit_menu_:
return true;
}
return super.onOptionsItemSelected(item);
}}
orderfullfill.xml
<?xml version="1.0" encoding="utf-8"?><android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="#+id/editTextNumber"
android:layout_width="177dp"
android:layout_height="56dp"
android:layout_marginStart="24dp"
android:layout_marginLeft="24dp"
android:layout_marginTop="80dp"
android:textSize="30sp"
app:layout_constraintBottom_toTopOf="#+id/listviewD"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0" />
<ListView
android:id="#+id/listviewD"
android:layout_width="354dp"
android:layout_height="221dp"
android:layout_marginBottom="12dp"
app:layout_constraintBottom_toTopOf="#+id/scanM2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.421"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="#+id/invoiceMaker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="#string/makeInvoice"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.75"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/scanM2" />
<TextView
android:id="#+id/nameView"
android:layout_width="333dp"
android:layout_height="52dp"
android:layout_marginLeft="25dp"
android:layout_marginTop="16dp"
android:layout_marginRight="25dp"
android:layout_marginBottom="303dp"
android:textSize="20dp"
app:layout_constraintBottom_toTopOf="#+id/scanM2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
<Button
android:id="#+id/orderdetailsBtn"
android:layout_width="119dp"
android:layout_height="54dp"
android:layout_marginTop="68dp"
android:layout_marginEnd="20dp"
android:layout_marginRight="20dp"
android:onClick="orderdetailsOnclick"
android:text="#string/showOrder"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.887"
app:layout_constraintStart_toEndOf="#+id/editTextNumber"
app:layout_constraintTop_toTopOf="parent" />
<Switch
android:id="#+id/scanM2"
android:layout_width="359dp"
android:layout_height="39dp"
android:layout_below="#id/listviewD"
android:layout_marginTop="232dp"
android:text="#string/highPowerScan"
android:textOff="No"
android:textOn="Yes"
android:textSize="27dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.461"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/orderdetailsBtn" />/android.support.constraint.ConstraintLayout>
orderResponder.java -> this is the class that the TSL reader communicates with via ASCII when reading RFID tags. The method Splicer adds the RFID values to the ArrayAdapter then notifyDataSetChanged(); is called. However nothing happens when this method is called from outside orderFullfill.class.
public class orderResponder extends Application implements IAsciiCommandResponder {
public static boolean tempList;
NoteHelper myDB;
public List<String> getReadInput() {
return readInput;
}
public void setReadInput(List<String> readInput) {
this.readInput = readInput;
}
public static List<String> readInput = new ArrayList<>();
#Override
public boolean isResponseFinished() {
return false;
}
#Override
public void clearLastResponse() {
}
#Override
public boolean processReceivedLine(String s, boolean b) throws Exception {
isitMe(s);
return false;
}
public List<String> getList()
{
return readInput;
}
public boolean isitMe (String input)
{
String firstFourChars = "";
//get first 4 chars to see if it contains "EP: "
if(input.length()>4)
{
firstFourChars = input.substring(0,4);
}
else
{
firstFourChars = input;
}
if(firstFourChars.equals("EP: "))// this is the inital identifier for the RFID tags
{
splicer(input);
return true;
}
else
{
return false;
}
}
//takes the last 24 characters removing the 'EP: '
public void splicer (String input)
{ // ArrayAdapter magic = orderFullfill.getAdapter(); THIS Didnt work
orderFullfill.adapter = new ArrayAdapter<String>(orderFullfill.mContext, android.R.layout.simple_spinner_item, orderFullfill.scannedPigs);
String output ="";
output = input.substring(input.length() -24);//maybe take 24 chars
if (orderFullfill.scannedPigs.size() == 0) {
// orderFullfill.scannedPigs.add(output);
orderFullfill.adapter.add(output);
orderFullfill.adapter.notifyDataSetChanged();
orderFullfill.numberofpigs = orderFullfill.numberofpigs - 1;
} else//if the list has values check you are not duplicating the values
{
if (orderFullfill.scannedPigs.contains(output)) {
return;//you are already in the list
} else//you are unique you can join the list
{
// orderFullfill.scannedPigs.add(output);
orderFullfill.adapter.add(output);
orderFullfill.adapter.notifyDataSetChanged();
orderFullfill.numberofpigs = orderFullfill.numberofpigs - 1;
}
}
}
public static String getWeeknow()
{
Date date = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");
String dateH = formatter.format(date);
//String dateH = date.toString();
String[] array = dateH.split("\\/");
String output ="";
int month = Integer.parseInt(array[1]);
month = month -1;
Calendar calendar = Calendar.getInstance();
calendar.set(Integer.parseInt(array[2]),month,Integer.parseInt(array[0]));
int weekOfyear = calendar.get(Calendar.WEEK_OF_YEAR);
output = String.valueOf(weekOfyear);
return output;
}}
create orderResponder constructor method. and send your activity(orderFullfill.this) and context to orderResponder as parameter. call like this;
((orderFullfill)activity).adapter = new ArrayAdapter<String>(context, android.R.layout.simple_spinner_item, ((orderFullfill)activity).scannedPigs);
((orderFullfill)activity).adapter.notifyDataSetChanged();
i hope it works.
I am getting tired fixing this problem. the app does not execute it always show me this error Binary XML file line #26: Error inflating class EditText. I don't understand how to fix this problem.
This is my activity_members.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.constraint.ConstraintLayout
android:id="#+id/newMeetScreen"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="visible"
tools:layout_editor_absoluteX="384dp">
<ImageView
android:id="#+id/imageView3"
android:layout_width="0dp"
android:layout_height="54dp"
android:src="#drawable/gradient"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="#+id/edit_search"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="11dp"
android:layout_marginTop="7dp"
android:layout_marginEnd="13dp"
android:layout_marginBottom="7dp"
android:background="#drawable/radius_edit_text"
android:drawableLeft="#drawable/ic_search"
android:drawablePadding="3dp"
android:hint="Поиск"
android:imeOptions="actionGo"
android:inputType="text"
android:textColor="#color/colorPrimaryDark"
android:textColorHint="#9b9a9a"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/btnDone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="#+id/btnDone"
android:layout_width="52dp"
android:layout_height="50dp"
android:background="#android:drawable/list_selector_background"
android:src="#drawable/ic_done"
android:textColor="#color/colorProjectTextWhite"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/edit_search"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
<android.support.v7.widget.RecyclerView
android:background="#color/colorBackgroundLight"
android:id="#+id/membersList"
android:scrollbars="vertical"
android:layout_marginTop="0dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
And this is my MembersActivity.java class.
public class MembersActivity extends AppCompatActivity {
private static final int REQUEST_CODE_READ_CONTACTS = 1;
private static boolean READ_CONTACTS_GRANTED = false;
RecyclerView members;
ArrayList<String> contacts = new ArrayList<String>();
ArrayList<String> tell = new ArrayList<String>();
UserAdapter adapter;
User user;
ArrayList<User> users = new ArrayList<>();
ImageButton btnDone;
StringBuilder stringBuilder = null;
EditText editSearch;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_members);
members = findViewById(R.id.membersList);
btnDone = findViewById(R.id.btnDone);
editSearch = findViewById(R.id.edit_search);
int hasReadContactPermission = ContextCompat.checkSelfPermission(Objects.requireNonNull(this), Manifest.permission.READ_CONTACTS);
if (hasReadContactPermission == PackageManager.PERMISSION_GRANTED) {
READ_CONTACTS_GRANTED = true;
} else {
ActivityCompat.requestPermissions(Objects.requireNonNull(this), new
String[]{Manifest.permission.READ_CONTACTS}, REQUEST_CODE_READ_CONTACTS);
}
if (READ_CONTACTS_GRANTED) {
getContacts();
}
btnDone.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
stringBuilder = new StringBuilder();
int i=0;
char ch = ',';
do{
User user = adapter.checkedUsers.get(i);
if(i == adapter.checkedUsers.size()-1) ch = '.';
stringBuilder.append(user.getName() + ch);
if(i != adapter.checkedUsers.size() -1) {
stringBuilder.append("\n");
}
i++;
} while (i < adapter.checkedUsers.size());
if(adapter.checkedUsers.size() > 0) {
Intent intent = new Intent();
intent.putExtra("names", stringBuilder.toString());
setResult(RESULT_OK, intent);
finish();
} else {
Toast.makeText(MembersActivity.this, "Пожалуйста, выберите друзей", Toast.LENGTH_LONG).show();
}
}
});
editSearch.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
adapter.getFilter().filter(charSequence);
return;
}
#Override
public void afterTextChanged(Editable editable) {
}
});
}
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case REQUEST_CODE_READ_CONTACTS:
if (grantResults.length > 0 && grantResults[0] ==
PackageManager.PERMISSION_GRANTED) {
READ_CONTACTS_GRANTED = true;
}
}
if (READ_CONTACTS_GRANTED) {
getContacts();
}
else {
Toast.makeText(this, "Требуется установить разрешения", Toast.LENGTH_LONG).show();
}
}
public void getContacts() {
Uri CONTENT_URI = ContactsContract.Contacts.CONTENT_URI;
String _ID = ContactsContract.Contacts._ID;
String DISPLAY_NAME = ContactsContract.Contacts.DISPLAY_NAME;
String HAS_PHONE_NUMBER = ContactsContract.Contacts.HAS_PHONE_NUMBER;
Uri PhoneCONTENT_URI =
ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
String Phone_CONTACT_ID =
ContactsContract.CommonDataKinds.Phone.CONTACT_ID;
String NUMBER = ContactsContract.CommonDataKinds.Phone.NUMBER;
StringBuffer output = new StringBuffer();
ContentResolver contentResolver = Objects.requireNonNull(this).getContentResolver();
Cursor cursor = contentResolver.query(CONTENT_URI, null, null, null, null);
if (cursor.getCount() > 0) {
while (cursor.moveToNext()) {
String contact_id = cursor.getString(cursor.getColumnIndex(_ID));
String name = cursor.getString(cursor.getColumnIndex(DISPLAY_NAME));
int hasPhoneNumber = Integer.parseInt(cursor.getString(cursor.getColumnIndex(HAS_PHONE_NUMBER)));
if (hasPhoneNumber > 0) {
contacts.add(name);
Cursor phoneCursor =
contentResolver.query(PhoneCONTENT_URI, null,
Phone_CONTACT_ID + " = ?", new String[]{contact_id}, null);
while (phoneCursor.moveToNext()) {
String phoneNumber = phoneCursor.getString(phoneCursor.getColumnIndex(NUMBER));
tell.add(phoneNumber);
}
}
}
}
if(tell.size() > contacts.size()) {
for(int i=0; i <contacts.size(); i++) {
user = new User(tell.get(i), contacts.get(i));
users.add(user);
}
} else {
for(int i=0; i < tell.size(); i++) {
user = new User(tell.get(i), contacts.get(i));
users.add(user);
}
}
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
adapter = new UserAdapter(users, this);
members.setLayoutManager(mLayoutManager);
members.setItemAnimator(new DefaultItemAnimator());
members.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
members.setAdapter(adapter);
}
#Override
protected void onDestroy() {
super.onDestroy();
}
#Override
public void onBackPressed() {
super.onBackPressed();
}
}
Please, help me fix this problem.
You need to set the width and height of your EditText in your layout file you cannot set these properties to 0dp. If you want to set this according to screen size then you can use layout_weight in your xml.Here you can know about layout_weight usage
If you are using constraintLayout in your layout you can see this link. Responsive layout designing with constraint layout
private class getArticles extends AsyncTask<Void, Void, Void> {
String url;
getArticles(String paramUrl) {
this.url = paramUrl;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
mProgressDialog = new ProgressDialog(App.this);
mProgressDialog.setMessage("Učitavanje artikala...");
mProgressDialog.setIndeterminate(false);
mProgressDialog.setCancelable(false);
mProgressDialog.show();
}
#Override
protected Void doInBackground(Void... params) {
arraylist = new ArrayList<>();
try {
Document document = Jsoup.connect(url).get();
Elements els = document.select("ul.category3 > li");
for (Element el : els) {
HashMap<String, String> map = new HashMap<>();
Elements slika = el.select("div.category3-image > a > img");
Elements naslov = el.select("div.category3-text > a.main-headline");
Element datum_noformat = el.select("div.category3-text > div.headlines-info > ul.headlines-info > li").first();
Element datum = datum_noformat.html(datum_noformat.html().replaceAll("Posted ", ""));
Elements desc = el.select("div.category3-text > p");
Elements link = el.select("div.category3-text > a.main-headline");
Element br_kom = el.select("div.category3-text > div.headlines-info > ul.headlines-info > li.comments-icon").first();
map.put("naslov", naslov.text());
map.put("datum", datum.text());
map.put("desc", desc.text());
map.put("ikona", slika.attr("src"));
map.put("link", link.attr("abs:href"));
map.put("brkom", br_kom.text());
arraylist.add(map);
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
listview = (ListView) findViewById(R.id.listview);
adapter = new ArtikliAdapter(App.this, arraylist);
listview.setAdapter(adapter);
mProgressDialog.dismiss();
}
I searched for a lot of codes for onlistview scrolling, but didn't know how to implement it. The problem is, when I call my asynctask, I have an url param,
like new getArticles("http://example.com").execute();
I want to implement an onscrolllistener, but it goes like this, my param is usually set to: http://www.example.com/category/categoryname/, so the second page goes like http://www.example.com/category/categoryname/page/2/, the third one goes http://www.example.com/category/categoryname/page/3/ and so on. Each page has got 7 items that need to be parsed.
How could I implement onscrolllistener, because of the url param?
Thanks in advance.
Base on this link, I have written following solution to add elements ( 30 elements at a time, i.e page size = 30) to listview asynchronously.
Create a class named EndlessListView as follows:
public class EndlessListView extends ListView implements OnScrollListener {
private View footer;
private boolean isLoading;
private EndlessListener listener;// listner
private EndlessAdapter endlessAdapter;// adapter.
private int maxNoOfElement;
public EndlessListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.setOnScrollListener(this);
}
public EndlessListView(Context context, AttributeSet attrs) {
super(context, attrs);
this.setOnScrollListener(this);
}
public EndlessListView(Context context) {
super(context);
this.setOnScrollListener(this);
}
public void setListener(EndlessListener listener) {
this.listener = listener;
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (getAdapter() == null)
return;
if (getAdapter().getCount() == 0 || getAdapter().getCount() <= 5)
return;
int l = visibleItemCount + firstVisibleItem;
if (l >= totalItemCount && !isLoading) {
// It is time to add new data. We call the listener
Log.e("LOAD", "DATA");
isLoading = true;
listener.loadData();
}
}
public void setMaxElemnt(int maxNoOfElement) {
this.maxNoOfElement = maxNoOfElement;
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
public void setLoadingView(int resId) {
LayoutInflater inflater = (LayoutInflater) super.getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
footer = (View) inflater.inflate(resId, null);
this.addFooterView(footer);
}
public void setAdapter(EndlessAdapter adapter) {
super.setAdapter(adapter);
this.endlessAdapter = adapter;
}
public void addNewData(List<Integer> data) {
endlessAdapter.setData(data);
endlessAdapter.notifyDataSetChanged();
isLoading = false;
}
public static interface EndlessListener {
public void loadData();
}
}
After this we have to create the adapter for it,called
EndlessAdapter
public class EndlessAdapter extends BaseAdapter {
private List<Integer> mIntegers;
private Context context;
public EndlessAdapter(Context context) {
this.context = context;
}
public void setData(List<Integer> mIntegers) {
this.mIntegers = mIntegers;
}
#Override
public int getCount() {
if (mIntegers == null)
return 0;
return mIntegers.size();
}
#Override
public Integer getItem(int index) {
if (mIntegers == null) {
return null;
} else {
return mIntegers.get(index);
}
}
#Override
public long getItemId(int arg0) {
// TODO Auto-generated method stub
return 0;
}
class ViewHolder {
TextView textView;
}
#Override
public View getView(int index, View view, ViewGroup viewGroup) {
ViewHolder holder;
if (view == null) {
holder = new ViewHolder();
view = ((LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE))
.inflate(R.layout.rows, viewGroup, false);
holder.textView = (TextView) view.findViewById(R.id.mtext);
view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
}
holder.textView.setText(getItem(index) + "");
return view;
}
}
Now in xml of the activity we could use EndlessListView(in com.example.stackoverflow package) as follows :
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.stackoverflow.EndlessListView
android:id="#+id/endlessListView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
After this step we need to make following change in the MainActivity
public class MainActivity extends Activity implements EndlessListener {
private EndlessAdapter adapter;
private EndlessListView endlessListView;
private List<Integer> data;
private int gStartIndex = 30;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
endlessListView = (EndlessListView) findViewById(R.id.endlessListView);
adapter = new EndlessAdapter(this);
data = new ArrayList<Integer>();
endlessListView.setLoadingView(R.layout.footer);
// endlessListView.showFooter();
endlessListView.setListener(this);
endlessListView.setAdapter(adapter);
gStartIndex = 0;
fillData(gStartIndex);
}
private void fillData(int startIndex) {
new AsyncLoadData().execute(startIndex);
}
#Override
public void loadData() {
fillData(gStartIndex);
}
private class AsyncLoadData extends AsyncTask<Integer, Integer, Void> {
#Override
protected Void doInBackground(Integer... params) {
int gendIndex = params[0] + 30;
gStartIndex = gendIndex;
/***
* Here you could add your n/w code. To simulate the n/w comm. i am
* adding elements to list and making it sleep for few sec.
* */
for (int i = params[0]; i < gendIndex; i++) {
publishProgress(i);
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
data.add(values[0]);
}
#Override
protected void onPostExecute(Void result) {
endlessListView.addNewData(data);
}
}
}
This custom ScrollListView that I just found has OnBottomReachedListener which you can implement from your Activity or Fragment and receive events when user hits the bottom of the page. You would also need to track the current page and when bottom is hit to download the next page. The latest data should be added to your existing ArrayList and you should call notifyDataSetChanged() on your adapter so ListView can render the new data. You don't have to create new adapter, you just need to update the data source which is your ArrayList.
If you support orientation change you would must to save in onSaveInstanceState() your current page number so when Activity or Fragment is recreated it can continue from correct page. And you would have to keep the ArrayList data source safe of configuration changes because you don't want to downloaded it again. I would suggest using the Fragment with setRetainInstance() set to true to persist ArrayList.
Here is my custom code for keeping data around using RetainFragment:
/**
* A simple non-UI Fragment that stores a single Object
* and is retained over configuration changes.
*/
public class RetainFragment<E extends Object> extends Fragment {
/** Object for retaining. */
private E mObject;
/**
* Empty constructor as per the Fragment documentation
*/
public RetainFragment() {}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Make sure this Fragment is retained over a configuration change
setRetainInstance(true);
}
/**
* Store a single object in this Fragment.
*
* #param object The object to store
*/
public void setObject(E object) {
mObject = object;
}
/**
* Get the stored object.
*
* #return The stored object
*/
public E getObject() {
return mObject;
}
}
Example of RetainFragment usage in your Activity:
FragmentManager fm = getFragmentManager();
mRetainFragment = (RetainFragment<ArrayList>) fm.findFragmentByTag(RETAIN_FRAG);
if (mRetainFragment == null) {
mRetainFragment = new RetainFragment<>();
mRetainFragment.setObject(new ArrayList());
fm.beginTransaction().add(mRetainFragment, RETAIN_FRAG).commit();
}
ArrayList yourArrayList = mRetainFragment.getObject();
// Now your ArrayList is saved accrossed configuration changes
here what you want fist add onscrolllistner to listview
boolean loading_flage = false;
yourlistview.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(
AbsListView view,
int scrollState) {
// TODO Auto-generated method stub
}
#Override
public void onScroll(AbsListView view,
int firstVisibleItem,
int visibleItemCount,
int totalItemCount) {
final int lastItem = firstVisibleItem
+ visibleItemCount;
if ((lastItem == totalItemCount)
& loading_flage == false) {
//this to prevent the list to make more than one request in time
loading_flage = true;
//you can put the index for your next page here
loadMore(int page);
}
}
});
and then in loading more after loading the page set the loading with false and parse the data add it to the data that you aleardy have
//notify the adapter
adapter.notifyDataSetChanged();
loading_flage = false;
You can achieve by adding the scrolllistener on listview and check condition if list last visible item is last in ArrayList then call the asynctask with incremented page number and update the listview,mean while add the view on listview and after getting the result from server remove the loading more view from the listview like this -
AndroidListViewLoadMoreActivity.java
public class AndroidListViewLoadMoreActivity extends Activity {
ArrayList<Country> countryList;
MyCustomAdapter dataAdapter = null;
int page = 0;
boolean loadingMore = false;
View loadMoreView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ListView listView = (ListView) findViewById(R.id.listView1);
loadMoreView = ((LayoutInflater)this
.getSystemService(Context.LAYOUT_INFLATER_SERVICE))
.inflate(R.layout.loadmore, null, false);
listView.addFooterView(loadMoreView);
//create an ArrayAdaptar from the String Array
countryList = new ArrayList<Country>();
dataAdapter = new MyCustomAdapter(this,
R.layout.country_info, countryList);
listView.setAdapter(dataAdapter);
//enables filtering for the contents of the given ListView
listView.setTextFilterEnabled(true);
listView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// When clicked, show a toast with the TextView text
Country country = (Country) parent.getItemAtPosition(position);
Toast.makeText(getApplicationContext(),
country.getCode(), Toast.LENGTH_SHORT).show();
}
});
listView.setOnScrollListener(new OnScrollListener(){
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
int lastInScreen = firstVisibleItem + visibleItemCount;
if((lastInScreen == totalItemCount) && !(loadingMore)){
String url = "http://10.0.2.2:8080/CountryWebService" +
"/CountryServlet";
grabURL(url);
}
}
});
String url = "http://example.com";
grabURL(url);
}
public void grabURL(String url) {
Log.v("Android Spinner JSON Data Activity", url);
new GrabURL().execute(url);
}
private class GrabURL extends AsyncTask<String, Void, String> {
private static final int REGISTRATION_TIMEOUT = 3 * 1000;
private static final int WAIT_TIMEOUT = 30 * 1000;
private final HttpClient httpclient = new DefaultHttpClient();
final HttpParams params = httpclient.getParams();
HttpResponse response;
private String content = null;
private boolean error = false;
private ProgressDialog dialog =
new ProgressDialog(AndroidListViewLoadMoreActivity.this);
protected void onPreExecute() {
dialog.setMessage("Getting your data... Please wait...");
dialog.show();
}
protected String doInBackground(String... urls) {
String URL = null;
loadingMore = true;
try {
URL = urls[0];
HttpConnectionParams.setConnectionTimeout(params, REGISTRATION_TIMEOUT);
HttpConnectionParams.setSoTimeout(params, WAIT_TIMEOUT);
ConnManagerParams.setTimeout(params, WAIT_TIMEOUT);
HttpPost httpPost = new HttpPost(URL);
//add name value pair for the country code
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
nameValuePairs.add(new BasicNameValuePair("page",String.valueOf(page)));
httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
response = httpclient.execute(httpPost);
StatusLine statusLine = response.getStatusLine();
if(statusLine.getStatusCode() == HttpStatus.SC_OK){
ByteArrayOutputStream out = new ByteArrayOutputStream();
response.getEntity().writeTo(out);
out.close();
content = out.toString();
} else{
//Closes the connection.
Log.w("HTTP1:",statusLine.getReasonPhrase());
response.getEntity().getContent().close();
throw new IOException(statusLine.getReasonPhrase());
}
} catch (ClientProtocolException e) {
Log.w("HTTP2:",e );
content = e.getMessage();
error = true;
cancel(true);
} catch (IOException e) {
Log.w("HTTP3:",e );
content = e.getMessage();
error = true;
cancel(true);
}catch (Exception e) {
Log.w("HTTP4:",e );
content = e.getMessage();
error = true;
cancel(true);
}
return content;
}
protected void onCancelled() {
dialog.dismiss();
Toast toast = Toast.makeText(AndroidListViewLoadMoreActivity.this,
"Error connecting to Server", Toast.LENGTH_LONG);
toast.setGravity(Gravity.TOP, 25, 400);
toast.show();
}
protected void onPostExecute(String content) {
dialog.dismiss();
Toast toast;
if (error) {
toast = Toast.makeText(AndroidListViewLoadMoreActivity.this,
content, Toast.LENGTH_LONG);
toast.setGravity(Gravity.TOP, 25, 400);
toast.show();
} else {
displayCountryList(content);
}
}
}
private void displayCountryList(String response){
JSONObject responseObj = null;
try {
Gson gson = new Gson();
responseObj = new JSONObject(response);
JSONArray countryListObj = responseObj.getJSONArray("countryList");
//countryList = new ArrayList<Country>();
if(countryListObj.length() == 0){
ListView listView = (ListView) findViewById(R.id.listView1);
listView.removeFooterView(loadMoreView);
}
else {
for (int i=0; i<countryListObj.length(); i++){
//get the country information JSON object
String countryInfo = countryListObj.getJSONObject(i).toString();
//create java object from the JSON object
Country country = gson.fromJson(countryInfo, Country.class);
//add to country array list
countryList.add(country);
dataAdapter.add(country);
}
page++;
dataAdapter.notifyDataSetChanged();
loadingMore = false;
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
Make MyCustomAdapter.java as adapter
private class MyCustomAdapter extends ArrayAdapter<Country> {
private ArrayList<Country> countryList;
public MyCustomAdapter(Context context, int textViewResourceId,
ArrayList<Country> countryList) {
super(context, textViewResourceId, countryList);
this.countryList = new ArrayList<Country>();
this.countryList.addAll(countryList);
}
private class ViewHolder {
TextView code;
TextView name;
TextView continent;
TextView region;
}
public void add(Country country){
Log.v("AddView", country.getCode());
this.countryList.add(country);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
Log.v("ConvertView", String.valueOf(position));
if (convertView == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.country_info, null);
holder = new ViewHolder();
holder.code = (TextView) convertView.findViewById(R.id.code);
holder.name = (TextView) convertView.findViewById(R.id.name);
holder.continent = (TextView) convertView.findViewById(R.id.continent);
holder.region = (TextView) convertView.findViewById(R.id.region);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Country country = this.countryList.get(position);
holder.code.setText(country.getCode());
holder.name.setText(country.getName());
holder.continent.setText(country.getContinent());
holder.region.setText(country.getRegion());
return convertView;
}
}
}
Create Country.Java as pojo -
public class Country {
String code = null;
String name = null;
String continent = null;
String region = null;
Double lifeExpectancy = null;
Double gnp = null;
Double surfaceArea = null;
int population = 0;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getContinent() {
return continent;
}
public void setContinent(String continent) {
this.continent = continent;
}
public String getRegion() {
return region;
}
public void setRegion(String region) {
this.region = region;
}
public Double getLifeExpectancy() {
return lifeExpectancy;
}
public void setLifeExpectancy(Double lifeExpectancy) {
this.lifeExpectancy = lifeExpectancy;
}
public Double getGnp() {
return gnp;
}
public void setGnp(Double gnp) {
this.gnp = gnp;
}
public Double getSurfaceArea() {
return surfaceArea;
}
public void setSurfaceArea(Double surfaceArea) {
this.surfaceArea = surfaceArea;
}
public int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
}
Create main.xml for main layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:orientation="vertical">
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content" android:padding="10dp"
android:text="#string/some_text" android:textSize="20sp" />
<ListView android:id="#+id/listView1" android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
Create country_info.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="6dip" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:text="Code: "
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/textView1"
android:layout_below="#+id/textView1"
android:text="Name: "
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="#+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/textView2"
android:layout_below="#+id/textView2"
android:text="Continent: "
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="#+id/textView4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/textView3"
android:layout_below="#+id/textView3"
android:text="Region: "
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="#+id/continent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/textView3"
android:layout_alignBottom="#+id/textView3"
android:layout_toRightOf="#+id/textView3"
android:text="TextView" />
<TextView
android:id="#+id/region"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/textView4"
android:layout_alignBottom="#+id/textView4"
android:layout_alignLeft="#+id/continent"
android:text="TextView" />
<TextView
android:id="#+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="#+id/textView3"
android:layout_toRightOf="#+id/textView3"
android:text="TextView" />
<TextView
android:id="#+id/code"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="#+id/textView2"
android:layout_alignLeft="#+id/name"
android:text="TextView" />
</RelativeLayout>
Footer View Text - loadmore.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:gravity="center_horizontal"
android:padding="3dp"
android:layout_height="fill_parent">
<TextView
android:id="#id/android:empty"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:gravity="center"
android:padding="5dp"
android:text="Loading more data..."/>
</LinearLayout>
Don't forget to mention internet permission with -
<uses-permission android:name="android.permission.INTERNET" />
First, you need a OnScrollListener method like this:
private OnScrollListener onScrollListener() {
return new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
int threshold = 1;
int count = listView.getCount();
if (scrollState == SCROLL_STATE_IDLE) {
if (listView.getLastVisiblePosition() >= count - threshold && pageCount < 2) {
Log.i(TAG, "loading more data");
// Execute LoadMoreDataTask AsyncTask
//It reached ListView bottom
getDataFromUrl(url_page2);
}
}
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
}
};
}
set List View scroll listener by listView.setOnScrollListener(onScrollListener());
I have a full tutorial post HERE! You can visit it!