I'm trying to change attribute to custom LinearLayout class, I set the option to class with:
MyBuilder option = new MyBuilder.Builder()
.image(...)
.setCardRadius(...)
.build());
Than i call in MainActivity
MyObject obj = (MyObject) findViewById(R.id.myObject);
obj.init(context, option);
But if I call multiple times obj.init(...) with different option the builder has old value setted so the view cannot change attribute correctly.
So my question is: can I reset Builder o LinearLayout before initializate new object?
This is my LinearLayout.java:
public class MyObject extends LinearLayout{
CardView card;
ImageView image;
float cardRadiusAttr;
View rootView;
AttributeSet attributeSet;
public void init(final Context context, final MyBuilder option){
if(option != null)
{
/*
Get attribute from XML
*/
TypedArray ta = context.obtainStyledAttributes(attributeSet, R.styleable.Card, 0, 0);
try {
cardRadiusAttr = ta.getDimension(R.styleable.Card_c_cardRadius, option.getCardRadius());
} finally {
ta.recycle();
}
/*
Set up xml object.
*/
card = (CardView) findViewById(R.id.card);
image = (ImageView) findViewById(R.id.image);
card.setRadius(cardRadiusAttr);
/**
* Check if Option is set
*/
if (option.isImage() != null) {
//Set Image
}
}else{
Log.e("Initialization", "Option View not initialize!");
}
}
public MyObject(Context context, AttributeSet attrs) {
super(context, attrs);
/*
Inflater custom layout to view.
*/
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
rootView = inflater.inflate(R.layout.Card, this, true);
attributeSet = attrs;
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
}
}
This is MyBuilder.java
public class MyBuilder {
private int mImage;
private float mCardRadius = 4f;
private MyBuilder(Builder builder)
{
mImage = builder.mImage;
mCardRadius = builder.mCardRadius;
}
public static class Builder{
private int mImage;
private float mCardRadius = 4f;
public Builder setCardRadius(float radius)
{
if(radius <= 0)
{
Log.e("CardRadius", "Impossible to set Card Radius lower than 0! Please Check it");
}
else {
mCardRadius = radius;
}
return this;
}
public Builder image(int image) {
if(image == 0)
{
Log.e("Image", "Impossible to set Image to 0! Please Check it");
}
else {
mImage = image;
}
return this;
}
public MyBuilder build() {
return new MyBuilder(this);
}
}
public int getImage() {
return mImage;
}
public float getCardRadius() {
return mCardRadius;
}
}
I finally found the issue.
In the init method of the MyObject you have to clean up the View after the previous use.
In this particular case, first, you pass one set of options. Based on them, View is adjusting Visibility of its controls (making button1, button2, etc. Visible). But when you pass another set of options - you have to erase all changes have been made before. (i.e. hide button1, button2, etc. and let the View to adjust Visibility of its controls once again)
Related
I have a recyclerview that displays a list of contacts. To differentiate between contacts that are also users of my app (let's refer to these as app-contacts) and all other contacts (non-app-contacts), i have made the typeface of all app-contacts bold (Typeface.BOLD), and non-app-contacts normal (Typeface.NORMAL). However, when the recyclerview gets filtered while searching for a contact, and app-contacts get displayed in certain rows (let's say rows 1 and 2) with a bold typeface, then those rows remain in a bold typeface. Even when i change the search, and non-app-contacts (which should be in a normal typeface) now occupy those rows (1 and 2), it’s in a bold typeface. Essentially rows 1 and 2 now remain in a bold typeface regardless of the type of contact being displayed in them.
Here is my recyclerview adapter. the onBindViewHolder is where i change the typeface. "is Suma Contact" boolean means the contact is an app contact.
public class SearchRecipientHintsAdapter extends RecyclerView.Adapter<SearchRecipientHintsAdapter.ViewHolder> {
private Context context;
private List<RecipientsContactItem> contactItems;
private final int SELECT_DROPOFF_REQUEST_CODE = 77;
public SearchRecipientHintsAdapter (Context context, List<RecipientsContactItem> contactItems) {
this.context = context;
this.contactItems = contactItems;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recipients_contact_row,parent,false);
return new ViewHolder(view, context);
}
#Override
public void onBindViewHolder(#NonNull SearchRecipientHintsAdapter.ViewHolder holder, int position) {
RecipientsContactItem contactItem = contactItems.get(position);
holder.name.setText(contactItem.getName());
holder.phoneNumber.setText(contactItem.getPhoneNumber());
if (contactItem.getImage() != null && !contactItem.getImage().isEmpty()) {
try {
Picasso.get().load(contactItem.getImage()).into(holder.image);
} catch (Throwable ignored) { }
} else {
holder.image.setImageDrawable(context.getResources().getDrawable(R.drawable.user_default_img));
}
if (contactItem.isVerified()) {
holder.verificationIcon.setVisibility(View.VISIBLE);
} else {
holder.verificationIcon.setVisibility(View.GONE);
}
if (contactItem.isSumaContact()) {
holder.name.setTypeface(holder.name.getTypeface(), Typeface.BOLD);
switch (contactItem.getPrivacy()) {
case "Public":
holder.publicIcon.setVisibility(View.VISIBLE);
holder.privateIcon.setVisibility(View.GONE);
holder.allowedIcon.setVisibility(View.GONE);
holder.inviteButton.setVisibility(View.GONE);
break;
case "Private":
holder.publicIcon.setVisibility(View.GONE);
holder.privateIcon.setVisibility(View.VISIBLE);
holder.allowedIcon.setVisibility(View.GONE);
holder.inviteButton.setVisibility(View.GONE);
break;
case "Allowed":
holder.publicIcon.setVisibility(View.GONE);
holder.privateIcon.setVisibility(View.GONE);
holder.allowedIcon.setVisibility(View.VISIBLE);
holder.inviteButton.setVisibility(View.GONE);
break;
}
} else {
holder.name.setTypeface(holder.name.getTypeface(), Typeface.NORMAL);
holder.inviteButton.setVisibility(View.VISIBLE);
holder.publicIcon.setVisibility(View.GONE);
holder.privateIcon.setVisibility(View.GONE);
holder.allowedIcon.setVisibility(View.GONE);
}
}
#Override
public int getItemCount() {
return contactItems.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView name;
private TextView phoneNumber;
private ImageView image;
private ImageView verificationIcon;
private Button inviteButton;
private ImageView publicIcon;
private ImageView privateIcon;
private ImageView allowedIcon;
public ViewHolder(#NonNull View itemView, Context ctx) {
super(itemView);
context = ctx;
name = itemView.findViewById(R.id.recipientsCRowNameID);
phoneNumber = itemView.findViewById(R.id.recipientsCRowPhoneID);
image = itemView.findViewById(R.id.recipientsCRowImageID);
verificationIcon = itemView.findViewById(R.id.recipientsCRowVerifiedID);
inviteButton = itemView.findViewById(R.id.recipientsCRowInviteID);
publicIcon = itemView.findViewById(R.id.recipientsCRowPublicID);
privateIcon = itemView.findViewById(R.id.recipientsCRowPrivateID);
allowedIcon = itemView.findViewById(R.id.recipientsCRowAllowedID);
itemView.setOnClickListener(v -> {
//Get position of row
int position = getAdapterPosition();
RecipientsContactItem contactItem = contactItems.get(position);
String uID = contactItem.getUID();
String name = contactItem.getName();
String phoneNumber = contactItem.getPhoneNumber();
String lat = contactItem.getLat();
String lng = contactItem.getLng();
boolean isSumaContact = contactItem.isSumaContact();
if (isSumaContact) {
if (contactItem.getPrivacy().equals("Public") || contactItem.getPrivacy().equals("Allowed")) {
Intent returnRecipientIntent = ((Activity) context).getIntent();
returnRecipientIntent.putExtra("uID", uID);
returnRecipientIntent.putExtra("name", name);
returnRecipientIntent.putExtra("phoneNumber", phoneNumber);
returnRecipientIntent.putExtra("lat", lat);
returnRecipientIntent.putExtra("lng", lng);
returnRecipientIntent.putExtra("isSumaContact", true);
((Activity) context).setResult(Activity.RESULT_OK, returnRecipientIntent);
((Activity) context).finish();
} else {
Toast.makeText(context, R.string.recipients_search_disallowed_toast, Toast.LENGTH_LONG).show();
}
} else {
Intent dropOffSearchIntent = new Intent(context, SelectDropoff.class);
((Activity) context).startActivityForResult(dropOffSearchIntent, SELECT_DROPOFF_REQUEST_CODE);
}
});
inviteButton.setOnClickListener(view -> {
Intent sendInvite = new Intent(android.content.Intent.ACTION_VIEW);
sendInvite.putExtra("address", contactItems.get(getAdapterPosition()).getPhoneNumber());
sendInvite.putExtra("sms_body", context.getResources().getString(R.string.recipients_invite_link));
sendInvite.setType("vnd.android-dir/mms-sms");
try {
context.startActivity(sendInvite);
} catch (Throwable t) {
Toast.makeText(context, "Sorry, invite not working. Please use the invite in your main menu", Toast.LENGTH_LONG).show();
}
});
}
}
#Override
public int getItemViewType(int position) {
return position;
}
public void updateWithSearchFilter (List<RecipientsContactItem> newList) {
contactItems = new LinkedList<>();
contactItems.addAll(newList);
notifyDataSetChanged();
}
}
Here is the onQueryTextChange() in setOnQueryTextListener() where i filter the search and pass the result/new list to the adapter above
public boolean onQueryTextChange(String newText) {
String userInput = newText.toLowerCase();
if (userInput.startsWith("0")) {userInput = userInput.substring(1);}
List<RecipientsContactItem> newList = new LinkedList<>();
for (RecipientsContactItem contactItem : sumaContacts) {
if (contactItem.getName().toLowerCase().contains(userInput) || contactItem.getPhoneNumber().contains(userInput)) {
newList.add(contactItem);
}
}
((SearchRecipientHintsAdapter) searchRHintsAdapter).updateWithSearchFilter(newList);
return true;
}
Shot 1:
the 2 contacts displayed are non-app contacts so their typeface is normal (not bold)
Shot 2. After filtering search to display an app-contact:
the first contact is an contact (bold typeface) and the second is a non-app contact (normal typeface - not bold)
Shot 3. After clearing search filter to display contacts in shot 1:
both contacts are non-app contacts and should be in a normal typeface (not bold). But the first contact is displayed as bold, because an app-contact (which is bold) was briefly displayed there (in shot 2) while filtering search
NB: The problem used to be caused by scrolling too. Till i #Override the getItemViewType() method of the Adapter
Initially, anytime i scroll the recyclerview, the Bold Typeface would be wrongly applied to rows/contacts that shouldn't be bold. Till i found a solution where i had to overrider the getItemViewType() method of the recyclerview adapter like this:
#Override
public int getItemViewType(int position) {
return position;
}
then it was fixed (for scrolling). till i realized that the problem persisted for filtering. So that's what i'm trying to fix now
The problem is
holder.name.setTypeface(holder.name.getTypeface(), Typeface.NORMAL);
When rebinding a viewholder with bold in place, holder.getTypeface() returns the bold typeface that was there earlier. Now, Typeface.NORMAL has the value 0. Here's the setTypeface() implementation from cs.android.com:
public void setTypeface(#Nullable Typeface tf, #Typeface.Style int style) {
if (style > 0) {
if (tf == null) {
tf = Typeface.defaultFromStyle(style);
} else {
tf = Typeface.create(tf, style);
}
setTypeface(tf);
// now compute what (if any) algorithmic styling is needed
int typefaceStyle = tf != null ? tf.getStyle() : 0;
int need = style & ~typefaceStyle;
mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
} else {
mTextPaint.setFakeBoldText(false);
mTextPaint.setTextSkewX(0);
setTypeface(tf);
}
}
Note the if (style > 0) part there. So, passing in Typeface.NORMAL will just set the typeface as-is, without doing any styling on it, so your bold style will stay bold.
To fix that, either pass in a null for typeface if that is appropriate for you, or reset the typeface to a default that fits your needs.
In addition, there's also a perf problem in your
#Override
public int getItemViewType(int position) {
return position;
}
This makes each row have its own specific view type. But you really only have one view type, so you don't need to override this method at all. Or if you do, you can return a constant value.
Right now i'm using the code below in my main activity
imageAnim.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
float x = imageAnim.getX(); // width - activity's field
Log.d("works", "" + x); //return right value
}
});
But I want to move it to a class, but I keep getting errors like cannot resolve symbol getViewTreeObserver. Is there a way to fix this?
You can pass your imageAnim to you class as a parameter passable by the constructor
for example, create a class named ClassHelper as the following :
public class ClassHelper {
private ImageView imageView;
private Context context;
private float x;
ClassHelper(ImageView imageView, Context context) {
this.context = context;
this.imageView = imageView;
}
void setViewTreeObserver() {
imageView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
x = imageView.getX();
}
});
}
public float getX() {
return x;
}
}
and call it in like this
ClassHelper classHelper = new ClassHelper(imageAnim,getApplicationContext());
classHelper.getViewTreeObserver();
//getX() method will return the value of X
Toast.makeText(this, "" + classHelper.getX(), Toast.LENGTH_SHORT).show();
I have 3 AutoCompleteTextView and I want to make a simple function to detect whenever it gain/loses focus so I'll hide an imageView.
Or maybe, whenever the keyboard is UP I want to hide my logo (imageView). When the keyboard is down, show the imageView again.
Code so far:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_main);
RelativeLayout layout = (RelativeLayout)findViewById(R.id.mainLayout);
for(int i=0; i< layout.getChildCount(); i++) {
View v = layout.getChildAt(i);
if(v instanceof AutoCompleteTextView) {
v.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View view, boolean b) {
// if(hasWindowFocus()) {
// ImageView logo = findViewById(R.id.imgLogo);
// logo.setVisibility(View.GONE);
// }
Toast.makeText(getApplicationContext(), "pimba", Toast.LENGTH_LONG).show();
}
});
}
}
}
Also, is it possible to create listeners outside this onCreate() ? Or creating everything inside this function is the right way to go?
Try this
Solution 1
Call this method in onCreate()
private void initKeyBoardListener() {
//Threshold for minimal keyboard height.
final int MIN_KEYBOARD_HEIGHT_PX = 150;
//Top-level window decor view.
final View decorView = getWindow().getDecorView();
// Register global layout listener.
decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
// Retrieve visible rectangle inside window.
private final Rect windowVisibleDisplayFrame = new Rect();
private int lastVisibleDecorViewHeight;
#Override
public void onGlobalLayout() {
decorView.getWindowVisibleDisplayFrame(windowVisibleDisplayFrame);
final int visibleDecorViewHeight = windowVisibleDisplayFrame.height();
if (lastVisibleDecorViewHeight != 0) {
if (lastVisibleDecorViewHeight > visibleDecorViewHeight + MIN_KEYBOARD_HEIGHT_PX) {
Log.d("Keyboard", "SHOW");
// Hide imageview
} else if (lastVisibleDecorViewHeight + MIN_KEYBOARD_HEIGHT_PX < visibleDecorViewHeight) {
// Show imageview
Log.d("Keyboard", "HIDE");
}
}
// Save current decor view height for the next call.
lastVisibleDecorViewHeight = visibleDecorViewHeight;
}
});
}
Solution 2
Use this Util class
import android.app.Activity;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import java.util.HashMap;
/**
* Created by Raman on 2/10/2017.
*/
public class KeyboardUtils implements ViewTreeObserver.OnGlobalLayoutListener {
private static HashMap<SoftKeyboardToggleListener, KeyboardUtils> sListenerMap = new HashMap<>();
private SoftKeyboardToggleListener mCallback;
private View mRootView;
private float mScreenDensity = 1;
private KeyboardUtils(Activity act, SoftKeyboardToggleListener listener) {
mCallback = listener;
mRootView = ((ViewGroup) act.findViewById(android.R.id.content)).getChildAt(0);
mRootView.getViewTreeObserver().addOnGlobalLayoutListener(this);
mScreenDensity = act.getResources().getDisplayMetrics().density;
}
public static void addKeyboardToggleListener(Activity act, SoftKeyboardToggleListener listener) {
removeKeyboardToggleListener(listener);
sListenerMap.put(listener, new KeyboardUtils(act, listener));
}
public static void removeKeyboardToggleListener(SoftKeyboardToggleListener listener) {
if (sListenerMap.containsKey(listener)) {
KeyboardUtils k = sListenerMap.get(listener);
k.removeListener();
sListenerMap.remove(listener);
}
}
public static void removeAllKeyboardToggleListeners() {
for (SoftKeyboardToggleListener l : sListenerMap.keySet())
sListenerMap.get(l).removeListener();
sListenerMap.clear();
}
#Override
public void onGlobalLayout() {
Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
mRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = mRootView.getRootView().getHeight() - (r.bottom - r.top);
float dp = heightDiff / mScreenDensity;
if (mCallback != null)
mCallback.onToggleSoftKeyboard(dp > 200);
}
private void removeListener() {
mCallback = null;
mRootView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
public interface SoftKeyboardToggleListener {
void onToggleSoftKeyboard(boolean isVisible);
}
}
And its usage
Call this in onCreate()
KeyboardUtils.addKeyboardToggleListener(getActivity(), new KeyboardUtils.SoftKeyboardToggleListener() {
#Override
public void onToggleSoftKeyboard(boolean isVisible) {
Log.d("keyboard", "keyboard visible: " + isVisible);
if (!isVisible) {
// show imageview
}
else
{
// hide imageview
}
}
});
I've been working on google's course sunshine app and wanted to put my personal touch in it so i made the user specify his city by using a hybrid of EditTextPreference and AutoCompleteTextView shown in here:
public class AutoCompleteEditTextPreference extends EditTextPreference {
private static String[] list;
private boolean isValid = true;
private Dialog dialog;
public AutoCompleteEditTextPreference(Context context) {
super(context);
}
public AutoCompleteEditTextPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AutoCompleteEditTextPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* the default EditTextPreference does not make it easy to
* use an AutoCompleteEditTextPreference field. By overriding this method
* we perform surgery on it to use the type of edit field that
* we want.
*/
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
// find the current EditText object
final EditText editText = (EditText) view.findViewById(android.R.id.edit);
// copy its layout params
ViewGroup.LayoutParams params = editText.getLayoutParams();
ViewGroup vg = (ViewGroup) editText.getParent();
String curVal = editText.getText().toString();
// remove it from the existing layout hierarchy
vg.removeView(editText);
// construct a new editable autocomplete object with the appropriate params
// and id that the TextEditPreference is expecting
mACTV = new AutoCompleteTextView(getContext());
mACTV.setLayoutParams(params);
mACTV.setId(android.R.id.edit);
mACTV.setText(curVal);
Arrays.sort(list);
isValid = isValid(mACTV.getText().toString());
mACTV.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
isValid = isValid(s.toString());
validate();
}
});
mACTV.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
isValid = isValid(mACTV.getText().toString());
validate();
}
});
ArrayAdapter<String> adapter = new ArrayAdapter<>(getContext(),
android.R.layout.simple_dropdown_item_1line, list);
mACTV.setAdapter(adapter);
// add the new view to the layout
vg.addView(mACTV);
}
private boolean isValid(CharSequence text) {
return !text.equals("") && Arrays.binarySearch(list, text.toString()) > 0;
}
#Override
protected void showDialog(Bundle state) {
super.showDialog(state);
validate();
}
private void validate() {
dialog = getDialog();
Toast.makeText(getContext(), Boolean.toString(dialog instanceof AlertDialog), Toast.LENGTH_SHORT).show();
if (dialog instanceof AlertDialog) {
Button btn = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
btn.setEnabled(isValid);
}
}
/**
* Because the baseclass does not handle this correctly
* we need to query our injected AutoCompleteTextView for
* the value to save
*/
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult && mACTV != null) {
String value = mACTV.getText().toString();
if (callChangeListener(value))
setText(value);
}
}
static void prepareCountriesList(Context context) {
List<String> lines = new ArrayList<>();
try {
InputStream inputStream = context.getAssets().open("cities.txt");
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = bufferedReader.readLine()) != null) {
lines.add(line);
}
} catch (IOException e) {
e.printStackTrace();
}
list = lines.toArray(new String[lines.size()]);
}
/**
* again we need to override methods from the base class
*/
public EditText getEditText() {
return mACTV;
}
private AutoCompleteTextView mACTV = null;
private final String TAG = "AutoCompleteEditTextPreference";
}
so everything was going great until the last part where i wanted to disable the ok button
private void validate() {
dialog = getDialog();
Toast.makeText(getContext(), Boolean.toString(dialog instanceof AlertDialog), Toast.LENGTH_SHORT).show();
if (dialog instanceof AlertDialog) {
Button btn = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
btn.setEnabled(isValid);
}
}
so i try the method getDialog();
and it returns a dialog that is not null and not an instance of AlertDialog
anyhelp please on getting the dialog properly or another way to disable the ok button programmatically
It's ok found the problem;
it was that i used
import android.support.v7.app.AlertDialog;
instead of
import android.app.AlertDialog;
thanks for anyone who tried to help
Let me briefly explain my activity structure. When the user enters the app, they are immediately sent to a fragment displaying a ScrollView, containing a series of layouts. The layouts are populated from data pulled from a server, with the first ten or so loaded on the creation of the fragment. As the user scrolls down the ScrollView, I would like new layouts to be added to the bottom of the ScrollView so that all of the data does not have to be gathered from the server at once, and so that the list of entries does not exceed the size it needs to be.
An example of this would be Facebook's news feed on their android app, or Instagram's scrolling pictures. The Gmail app also has this behavior, loading emails on an as needed basis. In all cases, new data is loaded as the user scrolls down the page.
My question is, how best can this type of behavior be implemented?
I am defining best as:
The most efficient
The most portable (functional on the most phones and most API versions)
The easiest to maintain and expand upon
Follows accepted Android standards and conventions
To be clear, I am not simply looking for a solution, but the best solution. I am aware that there are multiple ways to implement this behavior, but I am looking for a definitive solution. Please include a few sentences explaining why your method is the best. Thank you.
you can use this class for load more items from server
public class LoadMoreListView extends ListView implements OnScrollListener {
private static final String TAG = "LoadMoreListView";
/**
* Listener that will receive notifications every time the list scrolls.
*/
private OnScrollListener mOnScrollListener;
private LayoutInflater mInflater;
// footer view
private RelativeLayout mFooterView;
// private TextView mLabLoadMore;
private ProgressBar mProgressBarLoadMore;
// Listener to process load more items when user reaches the end of the list
private OnLoadMoreListener mOnLoadMoreListener;
// To know if the list is loading more items
private boolean mIsLoadingMore = false;
private int mCurrentScrollState;
public LoadMoreListView(Context context) {
super(context);
init(context);
}
public LoadMoreListView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public LoadMoreListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
mInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// footer
mFooterView = (RelativeLayout) mInflater.inflate(
R.layout.load_more_footer, this, false);
/*
* mLabLoadMore = (TextView) mFooterView
* .findViewById(R.id.load_more_lab_view);
*/
mProgressBarLoadMore = (ProgressBar) mFooterView
.findViewById(R.id.load_more_progressBar);
addFooterView(mFooterView);
super.setOnScrollListener(this);
}
#Override
public void setAdapter(ListAdapter adapter) {
super.setAdapter(adapter);
}
/**
* Set the listener that will receive notifications every time the list
* scrolls.
*
* #param l
* The scroll listener.
*/
#Override
public void setOnScrollListener(AbsListView.OnScrollListener l) {
mOnScrollListener = l;
}
/**
* Register a callback to be invoked when this list reaches the end (last
* item be visible)
*
* #param onLoadMoreListener
* The callback to run.
*/
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
mOnLoadMoreListener = onLoadMoreListener;
}
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (mOnScrollListener != null) {
mOnScrollListener.onScroll(view, firstVisibleItem,
visibleItemCount, totalItemCount);
}
if (mOnLoadMoreListener != null) {
if (visibleItemCount == totalItemCount) {
mProgressBarLoadMore.setVisibility(View.GONE);
// mLabLoadMore.setVisibility(View.GONE);
return;
}
boolean loadMore = firstVisibleItem + visibleItemCount >= totalItemCount;
if (!mIsLoadingMore && loadMore
&& mCurrentScrollState != SCROLL_STATE_IDLE) {
mProgressBarLoadMore.setVisibility(View.VISIBLE);
// mLabLoadMore.setVisibility(View.VISIBLE);
mIsLoadingMore = true;
onLoadMore();
}
}
}
public void onScrollStateChanged(AbsListView view, int scrollState) {
// bug fix: listview was not clickable after scroll
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) {
view.invalidateViews();
}
mCurrentScrollState = scrollState;
if (mOnScrollListener != null) {
mOnScrollListener.onScrollStateChanged(view, scrollState);
}
}
public void onLoadMore() {
Log.d(TAG, "onLoadMore");
if (mOnLoadMoreListener != null) {
mOnLoadMoreListener.onLoadMore();
}
}
/**
* Notify the loading more operation has finished
*/
public void onLoadMoreComplete() {
mIsLoadingMore = false;
mProgressBarLoadMore.setVisibility(View.GONE);
}
/**
* Interface definition for a callback to be invoked when list reaches the
* last item (the user load more items in the list)
*/
public interface OnLoadMoreListener {
/**
* Called when the list reaches the last item (the last item is visible
* to the user)
*/
public void onLoadMore();
}
}
this is the custom List view for loading more items from server.you should use LoadMoreListView class for this work.and u can call this class in xml like this
<com.broadpeak.nvoice.utils.LoadMoreListView
android:id="#+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
lmlv = (LoadMoreListView) view.findViewById(R.id.lv);
lmlv.setOnLoadMoreListener(new OnLoadMoreListener() {
#Override
public void onLoadMore() {
}
});
if any query then you can contact freely....
The method you are trying to use is called LazyLoading
As a suggested way in Android development you can make use of recycler views and RecyclerAdapter for attaining this.
First define an Recycler adapter and views and
then,you can set an onScrollListener to the recycle adapter and it should go like (Original Source)
public abstract class EndlessRecyclerOnScrollListener extends RecyclerView.OnScrollListener {
public static String TAG = EndlessRecyclerOnScrollListener.class.getSimpleName();
private int previousTotal = 0; // The total number of items in the dataset after the last load
private boolean loading = true; // True if we are still waiting for the last set of data to load.
private int visibleThreshold = 5; // The minimum amount of items to have below your current scroll position before loading more.
int firstVisibleItem, visibleItemCount, totalItemCount;
private int current_page = 1;
private LinearLayoutManager mLinearLayoutManager;
public EndlessRecyclerOnScrollListener(LinearLayoutManager linearLayoutManager) {
this.mLinearLayoutManager = linearLayoutManager;
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = recyclerView.getChildCount();
totalItemCount = mLinearLayoutManager.getItemCount();
firstVisibleItem = mLinearLayoutManager.findFirstVisibleItemPosition();
if (loading) {
if (totalItemCount > previousTotal) {
loading = false;
previousTotal = totalItemCount;
}
}
if (!loading && (totalItemCount - visibleItemCount)
<= (firstVisibleItem + visibleThreshold)) {
// End has been reached
// Do something
// like Load from server
current_page++;
onLoadMore(current_page);
loading = true;
}
}
public abstract void onLoadMore(int current_page);
}
As you can see you can make server requests at onScrolled method as commented which i think you can do with libraries like RetroFit or okHttp