My goal is to add my custom view inside LinearLayout.I have custom arrayList and I would to add custom views with for loop.Here is a my code snippet
public void replaceCustomView() {
for (int i = 0; i < insertDataItems().size(); i++) {
final LayoutInflater inflater = LayoutInflater.from(this);
final View reView = inflater.inflate(R.layout.item_parent_child_listing, null, false);
final TextView parentName = reView.findViewById(R.id.tv_parentName);
final ImageView headerImageView = reView.findViewById(R.id.header_imageView);
final LinearLayout linearLayout_childItems = reView.findViewById(R.id.ll_child_items);
final RelativeLayout headerLayout = reView.findViewById(R.id.header_layout);
final RelativeLayout headerImageLayout = reView.findViewById(R.id.header_image_layout);
parentName.setText(insertDataItems().get(i).getParentName());
if (insertDataItems().get(i).getChildDataItems() != null) {
headerImageLayout.setVisibility(View.VISIBLE);
headerImageLayout.setVisibility(View.VISIBLE);
for (int j = 0; j < insertDataItems().get(i).getChildDataItems().size(); j++) {
final LayoutInflater childInflater = LayoutInflater.from(this);
final View childView = childInflater.inflate(R.layout.z_history_child_item, null, false);
final TextView key = childView.findViewById(R.id.u_key);
final TextView value = childView.findViewById(R.id.u_value);
key.setText(insertDataItems().get(i).getChildDataItems().get(j).getKey());
value.setText(insertDataItems().get(i).getChildDataItems().get(j).getValue());
linearLayout_childItems.addView(childView);
}
} else {
headerImageLayout.setVisibility(View.GONE);
headerLayout.setBackgroundColor(Color.parseColor("#e8e8e8"));
}
linearLayout_childItems.setVisibility(View.GONE);
if (insertDataItems().get(i).getParentName().length() > 0) {
if (insertDataItems().get(i).isAllowDisable()) {
headerImageView.setVisibility(View.GONE);
linearLayout_childItems.setVisibility(View.VISIBLE);
} else {
headerImageView.setVisibility(View.VISIBLE);
linearLayout_childItems.setVisibility(View.GONE);
}
} else {
linearLayout_childItems.setVisibility(View.VISIBLE);
headerLayout.setVisibility(View.GONE);
}
replaceLayout.post(() -> replaceLayout.addView(reView));
}
}
I call this function like this
runOnUiThread(() -> replaceCustomView());
Custom views adding successfully,but my problem is that in a first time activity is slowly.Android need to much time to add view.My custom array's size is 20.Is it a any way to add views step by step ,not add all views each time?
What's a best practice ?
thanks
Please do not use the concept of adding runtime components when the items are more. This will cause the activity to share the memory with view rendering logic and you'll be able to recycle the views. this kind of implementation can be used only when there are a few (may be 2 to 5) items and can't be achieved with recyclerview due to some UI limitations.
Hence use a RecyclerView to load the items and you can use the concept of adding custom items inside a child item to achieve this functionality.
Related
I have a paging algorithm for the RecyclerView, which if done Scroll, a scrolling listener triggers and loads more elements. Initially, I assigned a number of elements to load in the request to the server by default that is 20 for my RecyclerView, which has a fixed size (not wrap_content).
I need to know the number of items that can be visible in the width/height of the defined RecyclerView before loading the data, to determine the amount of items to be requested in the initial load, given that with 20 items on some devices is not enough to activate the listener of the scroll and load more elements.
This is without considering the extra properties of the view, such as padding, margin etc ...
The solution can be in Java Android or Xamarin Android (not Forms) C#.
Update:
For you to have a clue, i have tried this and it works for me, only if I call it when the size of RecyclerView is assigned, inside the OnLayoutChange:
public int GetMaxVisibleItemCountFromRecyclerView(RecyclerView recyclerView)
{
if (recyclerView == null) return 0;
int Width = recyclerView.Width;
int Height = recyclerView.Height;
if (Width == 0 || Height == 0)
return 0;
var layoutManager = recyclerView.GetLayoutManager() as GridLayoutManager;
if (layoutManager == null) return 0;
int widthRatio = Width / layoutManager.SpanCount;
int quantity = (Height / widthRatio) * layoutManager.SpanCount;
return quantity;
}
This solution only works for RecyclerViews that use the GridLayoutManager. I have other RecyclerViews with defined sizes that also use the paging algorithm with a LinearLayoutManager.
I need a similar solution, that works with any LayoutManager of the RecyclerView and does not have to be called inside the OnLayoutChange, is this possible?
Instead of trying to replicate the computations that RecyclerView does to compute layout, let the system do the work for you. The following example lets RecyclerView lay out one item and measurements are taken from that. The dummy item is not displayed and is used just for measurement.
The advantage of this method is that we don't have to replicate what RecyclerView does to measure items. All key measurements are taken into account including padding, margins and decorations.
The following sample shows how this can be accomplished for GridLayoutManager and LinearLayoutManager. StaggeredGridLayoutManager and FlexboxLayoutManager are special cases and aren't taken into account here.
Here is a short video showing the results of this demo app showing that just one page of items was loaded.
MainActivity.java
public class MainActivity extends AppCompatActivity {
private List<String> mItems = new ArrayList<>();
private RecyclerView mRecycler;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecycler = findViewById(R.id.recyclerView);
// Sample for vertical LinearLayoutManager.
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
// Sample for GridLayoutManager with 4 spans. Each item comsumes 2 spans.
// GridLayoutManager layoutManager = new GridLayoutManager(this, 4);
// layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
// #Override
// public int getSpanSize(int position) {
// return 2;
// }
// });
// Add single dummy item that will be measured but not be displayed.
mItems.add("Dummy item");
RecyclerViewAdapter mAdapter = new RecyclerViewAdapter(mItems);
mRecycler.setLayoutManager(layoutManager);
mRecycler.setAdapter(mAdapter);
// Take measurements in OnPreDraawListener(). This could also be accomplished with
// mRecyclerView.post(new Runnable()...)
mRecycler.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
mRecycler.getViewTreeObserver().removeOnPreDrawListener(this);
// RecyclerView is laid out with single dummy entry. Get how many of this type
// of item can fit into the visible portion of the RecyclerView.
final int nItems = getInitialLoadCount(mRecycler);
Log.d(TAG, "<<<<Items per page=" + nItems);
// Don't need the dummy entry any more.
mItems.clear();
mRecycler.getAdapter().notifyDataSetChanged();
mItems = new ArrayList<>();
// Fake load...
loadInitialItems(nItems);
return false;
}
});
}
// Determine how many items will fill one screen of the RecyclerView. Call with the
// RecyclerView loaded with at least one item for measurement.
private int getInitialLoadCount(RecyclerView recyclerView) {
int itemsToLoad = 0;
RecyclerView.LayoutManager lm = recyclerView.getLayoutManager();
View firstChild = recyclerView.getChildAt(0);
if (lm instanceof LinearLayoutManager) {
Rect bounds = new Rect();
recyclerView.getDecoratedBoundsWithMargins(firstChild, bounds);
if (lm.canScrollVertically()) {
int recyclerHeightForItems = recyclerView.getHeight() - recyclerView.getPaddingTop()
- recyclerView.getPaddingBottom();
itemsToLoad = (recyclerHeightForItems + bounds.height() - 1) / bounds.height();
} else if (lm.canScrollHorizontally()) {
int recyclerWidthForItems = recyclerView.getWidth() - recyclerView.getPaddingLeft()
- recyclerView.getPaddingRight();
itemsToLoad = (recyclerWidthForItems + bounds.width() - 1) / bounds.width();
}
if (lm instanceof GridLayoutManager) {
// Adjust for GridLayoutManager. All items should to be the same number of spans.
GridLayoutManager glm = (GridLayoutManager) lm;
itemsToLoad *= glm.getSpanCount() / glm.getSpanSizeLookup().getSpanSize(0);
}
}
return itemsToLoad;
}
private void loadInitialItems(final int itemCount) {
// Simulate load of nItems...should be on non-UI thread.
new Thread(new Runnable() {
#Override
public void run() {
for (int i = 1; i <= itemCount; i++) {
sleep(250);
mItems.add("Item #" + i);
}
runOnUiThread(new Runnable() {
#Override
public void run() {
mRecycler.swapAdapter(new RecyclerViewAdapter(mItems), true);
}
});
}
}).start();
}
private static final String TAG = "MainActivity";
}
If your RecyclerView and your item width and height are defined in dp, then you should save their dimensions in dimen.xml. Then you can calculate how many items will fit like this:
float recyclerHeight = getResources().getDimension(R.dimen.recycler_height);
float itemHeight = getResources().getDimension(R.dimen.item_height);
int numOfItemsFit = (int) (recyclerHeight / itemHeight);
If your recyclerView isn't defined by dp, but the rest of the views in this layout are you can try to accomplish the same thing by decreasing the other views height from the total view height. You can check the total view height with:
public int getHeight() {
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
return size.y;
}
If you can't do the above you'll need to measure your RecyclerView inside of OnLayoutChange :( . You can do that with: recyclerView.getMeasuredHeight(); and recyclerView.getMeasuredWidth();
So I was able to have my textViews drag and drop properly. But when I close the application and re-open it doesn't save the position of the new textViews. In a previous application I was able to save data using SharedPreferences but I couldn't quite get it to work in this scenario. Any insight or help would be appreciated!
Thanks,
DragLinearLayout dragDropAndroidLinearLayout = (DragLinearLayout) findViewById(R.id.drag_drop_layout);
for (int i = 0; i < dragDropAndroidLinearLayout.getChildCount(); i++) {
View child = dragDropAndroidLinearLayout.getChildAt(i);
dragDropAndroidLinearLayout.setViewDraggable(child, child);
}
The code I'm using to implement the drag and drop feature. I guess I'm not sure what part in this code would I use to put into the SharedPreferences if that's the way I would even do it.
Try using setTag and getTag
for(i=0;i<5;i++){
final View items= View.inflate(this, R.layout.yourItemStyle, null);
items.setTag(i);
final TextView text1= (TextView) note.findViewById(R.id.textView1);
text1.setId(i);
dragLinearLayout.addDragView(items, text1,i);
dragLinearLayout.setOnViewSwapListener(new DragLinearLayout.OnViewSwapListener() {
#Override
public void onSwap(View firstView, int firstPosition, View secondView, int secondPosition) {
int ID= Integer.parseInt(String.valueOf(firstView.getTag()))
String strID = String.valueOf(ID);
Log.v("ID",strID );
}
});
}
I'm using a custom adapter with clickable items inside.The problem is that when i'm scrolling listview it re-runs my codes which exist in item click.
How can i disable listview doing this?
Here is an example of my adapter:
public override View GetView(int position, View convertView, ViewGroup parent)
{
View row = convertView;
if (row == null)
{
row = LayoutInflater.From(mContext).Inflate(Resource.Layout.InventoryPreview, null, false);
}
TextView txtInventoryName = row.FindViewById<TextView>(Resource.Id.txtInventoryName);
Button ExtraBtn = row.FindViewById<Button>(Resource.Id.ExtrasBtn);
txtInventoryName.Click += (sender, e) =>
{
var db = new SQLiteConnection(Connection.dpPath);
db.CreateTable<OrderPreviewClass>();
OrderPreviewClass tbl = new OrderPreviewClass();
Connection.InventoryItemID = mitems[position].InventoryItemID;
Connection.InventoryItemName = mitems[position].InventoryItemName;
Connection.RetailPrice = mitems[position].InventoryItemPrice;
Connection.Quantity = "1";
tbl.CategoryID = Connection.CategoryID;
tbl.InventoryItemID = Connection.InventoryItemID;
tbl.Description = Connection.InventoryItemName;
tbl.Quantity = Connection.Quantity;
tbl.Price = Connection.RetailPrice;
tbl.ExtrasPrice = "0";
tbl.RealPrice = Connection.RetailPrice;
tbl.Extras = ",";
db.Insert(tbl);
Toast toast = Toast.MakeText(mContext, "x1 " + txtInventoryName.Text, ToastLength.Short);
toast.Show();
};
ExtraBtn.Click += (sender, e) =>
{
Connection.InventoryItemID = mitems[position].InventoryItemID;
Connection.InventoryItemName = mitems[position].InventoryItemName;
Connection.RetailPrice = mitems[position].InventoryItemPrice;
Toast toast = Toast.MakeText(mContext, txtInventoryName.Text, ToastLength.Short);
toast.Show();
mContext.StartActivity(typeof(ExtrasPreviewMain));
};
return row;
}
How can i stop to re-run my code every time listview is scrolling? Should i use ClickListener?
The reason is that your cells get recycled, so every time the cell is recycled you are adding another event to the text control and button - events in C# are multicast delegates, so can fire multiple things.
Every time a cell is recycled another event handler is added.
The easies fix would be to extract both handlers into methods or local functions instead of lambdas, and always remove the handler before adding.
Something like:
void TextHandler(object sender, EventArgs args) =
{
var db = new SQLiteConnection(Connection.dpPath);
... // rest of the handler here
}
// remove the handler - doesn't matter if it's not already set
txtInventoryName.Click -= TextHandler;
// set the handler
txtInventoryName.Click += TextHandler;
I want to create a list of items using RecyclerView and want to expand particular item when clicked (Like in phone call list ). I want to achieve this without using any library. Can anyone help ?
Get child data list as a Member of Parent data in dataset.
And, at click event of RecyclerView row, use them like this..
here
mdataSet is main dataset for RecyclerView
final TitleHolder holder = (TitleHolder) h;
final Model model = (Model) mdataSet.get(position);
holder.txt_title.setText(model.getTitle());
holder.childItem = model;
holder.txt_title.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (model.getChildList() == null) {
//collapse
((Model) mdataSet.get(mdataSet.indexOf(holder.childItem))).isExpanded = false;
holder.arrow.startAnimation(AnimationUtils.loadAnimation(context, R.anim.arrow_reverse));
model.childList = new ArrayList<ModelData>();
int count = 0;
int pos = mdataSet.indexOf(holder.childItem);
while (mdataSet.size() > pos + 1 && mdataSet.get(pos + 1).type == Model.VIEW_CHILD) {
model.childList.add((ModelData) mdataSet.remove(pos + 1));
count++;
}
notifyItemRangeRemoved(pos + 1, count);
} else {
//expand
((Model) mdataSet.get(mdataSet.indexOf(holder.childItem))).isExpanded = true;
holder.arrow.startAnimation(AnimationUtils.loadAnimation(context, R.anim.arrow));
int pos = mdataSet.indexOf(holder.childItem);
int index = pos + 1;
for (ModelData i : model.getChildList()) {
mdataSet.add(index, i);
index++;
}
notifyItemRangeInserted(pos + 1, index - pos - 1);
model.childList = null;
}
}
});
if (((Model) mdataSet.get(mdataSet.indexOf(holder.childItem))).isExpanded) {
holder.arrow.startAnimation(AnimationUtils.loadAnimation(context, R.anim.arrow));
}
Here, I will add child data to Main dataset at click event on txt_title
Again, use Title(parent) and data(child) as two different ViewTypes like this
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TITLE) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_expand_title, parent, false);
return new TitleHolder(itemView);
} else {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.row_wallet_history, parent, false);
return new DataHolder(itemView);
}
}
OR
If your child view is fix (which you want to expand/collapse) then wrap them inside layout and, make that Layout visible/Gone with animation in order to achieve expand collapse effect
Refere this link to make them animated
I have method CreateView, which dynamically creates View and fill them with data from SharedPrefernces file.
First for (File loadFile : loadFiles) work correctly. It fills data in fields in created View that 100% are in the file SharedPrefernces.
Second for (Map.Entry entry : usersPresets.getAll().entrySet()). It fills data in created Views, but not the fact that they are in SharedPrefences file. And here I got problem.
public void CreateView(File[] loadFiles) {
if (presetsStorage.exists()) {
for (File loadFile : loadFiles) {
//noinspection SpellCheckingInspection
if (!loadFile.getName().contains("userpreset_")) {
//noinspection UnnecessaryContinue
continue;
} else {
if (loadFile.isFile()) {
String presetsName = loadFile.getName();
presetsName = presetsName.substring(0, presetsName.length() - 4);
usersPresets = getSharedPreferences(presetsName, MODE_PRIVATE);
presetsContainer = (LinearLayout) findViewById(R.id.presetsContainer);
View userPresetsView = getLayoutInflater()
.inflate(R.layout.preset_fragment, presetsContainer, false);
userPresetsView.findViewById(R.id.presetFragMenubtn)
.setOnClickListener(viewClickListener);
((CircularSeekBar) userPresetsView.findViewById(R.id.presetFragA)).
setProgress(usersPresets.getInt("a", 0));
((CircularSeekBar) userPresetsView.findViewById(R.id.presetFragB)).
setProgress(usersPresets.getInt("b", 0));
((CircularSeekBar) userPresetsView.findViewById(R.id.presetFragC)).
setProgress(usersPresets.getInt("c", 0));
TextView presetFragName = (TextView) userPresetsView
.findViewById(R.id.userPresetName);
presetFragName.setText(presetsName);
presetFragName.setOnClickListener(viewClickListener2);
presetsContainer.addView(userPresetsView);
presetFragName.setText(presetsName.substring(11));
//HERE I HAVE A PROBLEM
for (Map.Entry<String, ?> entry : usersPresets.getAll().entrySet()) {
if (arr.contains(entry.getKey())) {
// noinspection UnnecessaryContinue
continue;
} else {
CardView flavor_cardView = (CardView) findViewById(R.id.flavor_cardView);
final View usersFlavorsPreset = getLayoutInflater()
.inflate(R.layout.preset_userflavor_fragment, flavor_cardView, false);
((TextView) usersFlavorsPreset.findViewById(R.id.tvUserFlavorPresetName))
.setText(entry.getKey());
((TextView) usersFlavorsPreset.findViewById(R.id.tvUserFlavorPresetValue)).
setText(String.valueOf(entry.getValue()));
flavor_cardView.addView(usersFlavorsPreset);
}
}
}
}
}
}
}
I need that would, at presence of this data, created a new row, depending on the amount of data in the SharedPrefernces. Now, it works, but creates only one row and the values rewrites and displays only the data that were in the last iteration of the passage of second for.
Screenshots, where I tried to explain the problem:
View - screenshot
Content of aaa.xml - screenshot 2
Content of test.xml - screenshot 3
Tell me please, where I need to place for that would have the values not overwritten in the same location.
I hope that explained at least a little understandable. If you do not know - I will try to describe the problem again.
UPDATE: I added to the CardView the LinearLayout, and all the elements are now displayed in one place. So another question - how to place them in the right box?
for (Map.Entry<String, ?> entry : usersPresets.getAll().entrySet()) {
if (arr.contains(entry.getKey())) {
// noinspection UnnecessaryContinue
continue;
} else {
LinearLayout flavor_linear = (LinearLayout) findViewById(R.id.flavor_linear);
final View usersFlavorsPreset = getLayoutInflater().
inflate(R.layout.preset_userflavor_fragment, flavor_linear, false);
((TextView) usersFlavorsPreset.
findViewById(R.id.tvUserFlavorPresetName)).setText(entry.getKey());
((TextView) usersFlavorsPreset.
findViewById(R.id.tvUserFlavorPresetValue)).setText(String.valueOf(entry.getValue()));
flavor_linear.addView(usersFlavorsPreset);
}
}