In my test app I disable expansion of AppBarLayout when it is collapsed (by scrolling RecycleView). I do that by adding addOnOffsetChangedListener to AppBarLayout.
However, when I click EditText it expands again but, I do not want it to expand.
How to disable the expansion of AppBarLayout when I click EditText?
I have put the whole code, so that everyone can copy/paste the code, create new project and test this fast.
Here is how the .gif looks
Here is XML code:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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.design.widget.AppBarLayout
android:id="#+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="400dp"
android:fitsSystemWindows="true"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleTextAppearance="#android:color/transparent"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="#drawable/beachcroatia"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/recycleView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="?attr/actionBarSize"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
<LinearLayout
android:id="#+id/messages_linear_layout"
android:layout_width="match_parent"
android:layout_height="58dp"
android:layout_gravity="bottom"
android:background="#E1F5FE"
android:orientation="horizontal"
tools:layout_editor_absoluteY="453dp">
<EditText
android:id="#+id/editText_messageBox"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
android:layout_gravity="center"
android:layout_weight="0.85"
android:hint="Enter a message"/>
<ImageView
android:id="#+id/messages_sendArrow"
android:layout_width="0dp"
android:layout_height="30dp"
android:layout_gravity="center"
android:layout_weight="0.15"
android:src="#drawable/ic_send_message_" />
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
Here is full MainActivity code:
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerChat;
private AdapterChat mAdapterChat;
CollapsingToolbarLayout collapsingToolbarLayout;
AppBarLayout appBarLayout;
ImageView sendMessageImageViewButton;
EditText editTextMessage;
private List<Message> messagesList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
collapsingToolbarLayout = findViewById(R.id.collapsing_toolbar);
appBarLayout = findViewById(R.id.app_bar_layout);
sendMessageImageViewButton = findViewById(R.id.messages_sendArrow);
editTextMessage = findViewById(R.id.editText_messageBox);
messagesList = new ArrayList<>();
messagesList.addAll(getMessagesList());
mRecyclerChat = findViewById(R.id.recycleView);
LinearLayoutManager manager = new LinearLayoutManager(this);
mRecyclerChat.setLayoutManager(manager);
mAdapterChat = new AdapterChat(this, messagesList);
mRecyclerChat.setAdapter(mAdapterChat);
//move to the last item in recycleview
mRecyclerChat.getLayoutManager().scrollToPosition(mAdapterChat.getMessagesObekt().size() - 1);
editTextMessage.requestFocus();
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if (Math.abs(verticalOffset) == appBarLayout.getTotalScrollRange()) {
// Collapsed
Toast.makeText(MainActivity.this, "Collapsed", Toast.LENGTH_SHORT).show();
// disable expanding
AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) collapsingToolbarLayout.getLayoutParams();
params.setScrollFlags(0);
} else if (verticalOffset == 0) {
Toast.makeText(MainActivity.this, "Extend", Toast.LENGTH_SHORT).show();
// Expanded
} else {
// Somewhere in between
}
}
});
//sending new message and updating recycleview
sendMessageImageViewButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "Message Send", Toast.LENGTH_SHORT).show();
mAdapterChat.updateLastMessage(new Message(editTextMessage.getText().toString()));
editTextMessage.getText().clear();
mRecyclerChat.getLayoutManager().scrollToPosition(mAdapterChat.getMessagesObekt().size() - 1);
}
});
}
//ading some items to a list
private List<Message> getMessagesList() {
List<Message> mMessages = new ArrayList<>();
for (int i = 0; i < 200; i++) {
mMessages.add(new Message("message " + i));
}
return mMessages;
}
}
Here is my message class where i add dummy data to show in RecycleView
public class Message {
private String message;
public Message(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Here is RecycleView Adapter code:
package com.example.petar.collapsingtolbartestiramo;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class AdapterChat extends RecyclerView.Adapter<AdapterChat.ChatHolder> {
private LayoutInflater mInflater;
private List<Message> messagesObekt;
public AdapterChat(Context context, List<Message> listOfMassages) {
mInflater = LayoutInflater.from(context);
messagesObekt = new ArrayList<>();
messagesObekt.addAll(listOfMassages);
notifyDataSetChanged();
}
public void updateChat(List<Message> porukeObjekt){
this.messagesObekt.addAll(porukeObjekt);
notifyDataSetChanged();
}
public void updateLastMessage(Message porukeObjekt) {
this.messagesObekt.add(porukeObjekt);
//notifyDataSetChanged();
notifyItemInserted(messagesObekt.size());
}
public List<Message> getMessagesObekt() {
return messagesObekt;
}
#Override
public ChatHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.list_message, parent, false);
ChatHolder holder = new ChatHolder(view, messagesObekt);
return holder;
}
#Override
public void onBindViewHolder(ChatHolder holder, int position) {
holder.textViewMessage.setText(messagesObekt.get(position).getMessage());
}
#Override
public int getItemCount() {
return messagesObekt.size();
}
public static class ChatHolder extends RecyclerView.ViewHolder {
TextView textViewMessage;
public ChatHolder(View itemView, List<Message> messagesObekt) {
super(itemView);
textViewMessage = itemView.findViewById(R.id.rv_message);
}
}
}
Here is RecycleView item XML code (list_message.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:orientation="vertical">
<TextView
android:id="#+id/rv_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Poruka Ovdje"
android:textSize="14sp"/>
</LinearLayout>
:
EDIT
I tried adding focus listener to EditText but, that did not helped. This is how i try:
editTextMessage.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if(hasFocus){
appBarLayout.setExpanded(false);
AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) collapsingToolbarLayout.getLayoutParams();
params.setScrollFlags(0);
}
}
});
I do not know is the best solution, but it works nice. If someone has better solution, feel free to write it.
There is indeed a better, cleaner and simpler solution.
The culprit that leads to this "issue" is actually a feature that was added by Google inside AppBarLayout.ScrollingViewBehavior. That's the one you set via app:layout_behavior="#string/appbar_scrolling_view_behavior". Inside that class, onRequestChildRectangleOnScreen calls setExpanded(false, !immediate) whenever the keyboard is shown. You simply override this method and return false to disable this default behavior. Add this class to your project:
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import com.google.android.material.appbar.AppBarLayout;
public class FixedScrollingViewBehavior extends AppBarLayout.ScrollingViewBehavior {
public FixedScrollingViewBehavior() {
super();
}
public FixedScrollingViewBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onRequestChildRectangleOnScreen(#NonNull CoordinatorLayout parent,
#NonNull View child, #NonNull Rect rectangle, boolean immediate) {
return false;
}
}
Then just use this new class by changing app:layout_behavior="#string/appbar_scrolling_view_behavior" to app:layout_behavior="your.source.package.FixedScrollingViewBehavior".
I have find the answer. I do not know is the best solution, but it works nice. If someone has better solution, feel free to write it.
So, what i done is i put the whole AppBarLayout params to 0 when i click EditText or when EditText get focus.
So, when EditText is clicked, AppBarLayout is still there but, now his height is 0 and it is not visible.
This do the trick:
//Collaps AppBarLayout
public void collapsAppBarLayout(){
CoordinatorLayout.LayoutParams params =(CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
params.height = 0; // setting new param height to 0
//setting params to appbarLayout (now AppBarLayout is 0)
appBarLayout.setLayoutParams(params);
appBarLayout.setExpanded(false);
}
Also, if you want to have expand option, you need to save height of previous params and then just add that height again.
So, this is a code how i done it:
Fist, when i scroll RecycleView i disable expand of appBarLayout using addOnOffsetChangedListener. However i also save params height for future use.
int sizeParams; //save params height
appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if (Math.abs(verticalOffset) == appBarLayout.getTotalScrollRange()) {
// Collapsed
Toast.makeText(MainActivity.this, "Collapsed", Toast.LENGTH_SHORT).show();
// disable expanding and scroll
AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) collapsingToolbarLayout.getLayoutParams();
params.setScrollFlags(0);
//saving height for future use
sizeParams = params.height;
} else if (verticalOffset == 0) {
Toast.makeText(MainActivity.this, "Extend", Toast.LENGTH_SHORT).show();
// Expanded
} else {
// Somewhere in between
}
}
});
Now i make this 2 method which i call when i need expand/collaps.
//Collaps AppBarLayout
public void collapsAppBarLayout(){
CoordinatorLayout.LayoutParams params =(CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
sizeParams = params.height;//save params height for future use
params.height = 0; // setting new param height to 0
//setting params to appbarLayout (now AppBarLayout is 0
appBarLayout.setLayoutParams(params);
appBarLayout.setExpanded(false);
}
//Expand AppBarLayout
public void expandAppBarLayout(){
CoordinatorLayout.LayoutParams params =(CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
params.height = 3*sizeParams; // HEIGHT
appBarLayout.setLayoutParams(params); //add height to appbarlayout
//enable scroll and expand
AppBarLayout.LayoutParams params1 = (AppBarLayout.LayoutParams) collapsingToolbarLayout.getLayoutParams();
params1.setScrollFlags(1);
appBarLayout.setExpanded(true);//expand
}
Related
I got a simple layout with TabLayout and PageViewer but can't get them to work together
<com.google.android.material.tabs.TabLayout
android:id="#+id/sliding_tabs_emoji"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
app:tabMode="scrollable"
app:tabGravity="fill"
app:tabIndicatorColor="#color/gray"
app:tabIndicatorHeight="1dp"
android:paddingBottom="2dp"/>
<androidx.viewpager.widget.ViewPager
android:id="#+id/viewpager_emoji"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/red"/>
In my Activity OnCreate initializing the following
//Fragment array
var fragments = new AndroidX.Fragment.App.Fragment[]
{
Library.Fun.Emoji.Fragments.Recent.NewInstance(),
Library.Fun.Emoji.Fragments.People.NewInstance(),
};
//Tab title array
var titles = Android.Runtime.CharSequence.ArrayFromStringArray(new[] {
"Recent" ,
"People"
});
var viewPager = FindViewById<ViewPager>(Resource.Id.viewpager_emoji);
//viewpager holding fragment array and tab title text
viewPager.Adapter = new EmojiTabsPagerAdapter(SupportFragmentManager, fragments, titles);
// Give the TabLayout the ViewPager
TAB_Layout.SetupWithViewPager(viewPager);
Toast.MakeText(this, "SET", ToastLength.Short);
TAB_Layout.GetTabAt(0).SetIcon(Resource.Drawable.ic_camera);
TAB_Layout.GetTabAt(1).SetIcon(Resource.Drawable.ic_camera);
Where the Adapter is simple as it could be as following
public class EmojiTabsPagerAdapter : FragmentPagerAdapter
{
private readonly AndroidX.Fragment.App.Fragment[] fragments;
private readonly ICharSequence[] titles;
public EmojiTabsPagerAdapter(AndroidX.Fragment.App.FragmentManager fm, AndroidX.Fragment.App.Fragment[] fragments, ICharSequence[] titles) : base(fm)
{
this.fragments = fragments;
this.titles = titles;
}
public override int Count
{
get
{
return fragments.Length;
}
}
public override AndroidX.Fragment.App.Fragment GetItem(int position)
{
return fragments[position];
}
public override ICharSequence GetPageTitleFormatted(int position)
{
//return titles[position];
return null;
}
}
Tabs are appearing but Fragments are not loading, The results are the following
Where it should load RECENT and PEOPLE fragments on each tab, my Fragment XML is the following
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
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="300dp"
android:background="#color/green">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="RECENTS"/>
</RelativeLayout>
And fragment's code is the following (for RECENT)
public class Recent : AndroidX.Fragment.App.Fragment
{
public static Recent NewInstance()
{
var frag = new Recent { Arguments = new Bundle() };
return frag;
}
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your fragment here
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.Inflate(Resource.Layout.fragment_emoji_recent, container, false);
return view;
}
}
Any Idea what am I doing wrong?
I use two Fragments: TabFragment1, TabFragment2 to test. In your code, you did provide the code about SetupWithViewPager.
The code below works well for me.
void setupViewPager(Android.Support.V4.View.ViewPager viewPager)
{
var adapter = new Adapter(SupportFragmentManager);
adapter.AddFragment(new TabFragment1(), "First Fragment");
adapter.AddFragment(new TabFragment2(), "Second Fragment");
viewPager.Adapter = adapter;
viewpager.Adapter.NotifyDataSetChanged();
//viewpager.OffscreenPageLimit(4);
}
In MainActivity, you could set like below.
var tabLayout = FindViewById<TabLayout>(Resource.Id.tabs);
tabLayout.SetupWithViewPager(viewpager);
You could download the source file from the link: https://www.c-sharpcorner.com/article/xamarin-android-create-viewpager-tablayout-floatingactionbutton-supportacti/
Did this work before moving to AndroidX?
I think you may need to pass ChildFragmentManager instead of FragmentManager to your PagerAdapter.
I am using a spinner and an arrayAdapter to populate items in spinner. I want to make the spinner multi-selectable but I just select one row.I searched in Google, solution available in Java, but i don't have any idea on how to implement it in Xamarin.
My code is as below,
adapterList= new ArrayAdapter<string>(this, Android.Resource.Layout.SimpleListItemMultipleChoice);
spnMultiTest.Adapter = adapterList;
I want to make the spinner multi-selectable but I just select one row.I searched in Google, solution available in Java, but i don't have any idea on how to implement it in Xamarin.
Basically I just translate the codes from Android Spinner with multiple choice
to Xamarin codes. I have tested it and it works fine:
MultiSpinner.cs:
public interface MultiSpinnerListener
{
void onItemsSelected(bool[] selected);
}
public class MultiSpinner : Spinner, IDialogInterfaceOnMultiChoiceClickListener, IDialogInterfaceOnCancelListener
{
Context _context;
private List<String> items;
private bool[] selected;
private String defaultText;
private MultiSpinnerListener listener;
public MultiSpinner(Context context) : base(context)
{
_context = context;
}
public MultiSpinner(Context context, IAttributeSet arg1) : base(context, arg1)
{
_context = context;
}
public MultiSpinner(Context context, IAttributeSet arg1, int arg2) : base(context, arg1, arg2)
{
_context = context;
}
public void OnClick(IDialogInterface dialog, int which, bool isChecked)
{
if (isChecked)
selected[which] = true;
else
selected[which] = false;
}
public override void OnClick(IDialogInterface dialog, int which)
{
dialog.Cancel();
}
public override bool PerformClick()
{
AlertDialog.Builder builder = new AlertDialog.Builder(_context);
builder.SetMultiChoiceItems(
items.ToArray(), selected, this);
builder.SetPositiveButton("OK",this);
builder.SetOnCancelListener(this);
builder.Show();
return true;
}
public void SetItems(List<String> items, String allText,
MultiSpinnerListener listener)
{
this.items = items;
this.defaultText = allText;
this.listener = listener;
// all selected by default
selected = new bool[items.Count];
for (int i = 0; i < selected.Length; i++)
selected[i] = true;
ArrayAdapter<string> adapter = new ArrayAdapter<string>(_context,Resource.Layout.simple_spinner_item,Resource.Id.tv_item,new string[] { allText });
// all text on the spinner
//ArrayAdapter<String> adapter = new ArrayAdapter<String>(_context,Resource.Layout.simple_spinner_item, new String[] { allText });
Adapter = adapter;
}
public void OnCancel(IDialogInterface dialog)
{
Java.Lang.StringBuffer spinnerBuffer = new Java.Lang.StringBuffer();
bool someUnselected = false;
for (int i = 0; i < items.Count; i++)
{
if (selected[i] == true)
{
spinnerBuffer.Append(items[i]);
spinnerBuffer.Append(", ");
}
else
{
someUnselected = true;
}
}
String spinnerText;
if (someUnselected)
{
spinnerText = spinnerBuffer.ToString();
if (spinnerText.Length > 2)
spinnerText = spinnerText.Substring(0, spinnerText.Length - 2);
}
else
{
spinnerText = defaultText;
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(_context,Resource.Layout.simple_spinner_item,Resource.Id.tv_item,new string[] { spinnerText });
Adapter = adapter;
if (listener != null)
{
listener.onItemsSelected(selected);
}
}
}
simple_spinner_item.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/tv_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
Update:
Here is the codes for using this MultiSpinner:
Main.axml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<MultiSpinner.MultiSpinner
android:id="#+id/mSpinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
MainActivity.cs:
public class MainActivity : Activity
{
MultiSpinner mSpinner;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
mSpinner = FindViewById<MultiSpinner>(Resource.Id.mSpinner);
List<string> items = new List<string> {
"Android",
"iOS",
"UWP"
};
mSpinner.SetItems(items, "AllText", null);
}
}
I have to display the following table:
So I want to know what is the best widget to use for the table. I want the table to be dynamic I mean if I choose some other group from the dropdown the number of items in the table to increase or to decrease and the total price to change depending on what I have in column price. What widget is the best widget to achieve that?
RecyclerView is the best UI control to achieve it. Because RecyclerView is Adapter pattern, so you can use the adapter to achieve dynamic what you have said.
I want the table to be dynamic I mean if I choose some other group from the dropdown the number of items in the table to increase or to decrease and the total price to change depending on what I have in column price
I have made a demo for you, you can test it and do some changes for your project:
Three .axml files:
Main.axml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<Button
android:id="#+id/bt1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="group1"/>
<Button
android:id="#+id/bt2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="group2"/>
<Button
android:id="#+id/bt3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="group3"
/>
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
item_layout, every item's layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/item_tv"
android:layout_width="match_parent"
android:textSize="30dp"
android:layout_height="wrap_content" />
</LinearLayout>
item_layout, total price's layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/foot_tv"
android:gravity="end"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
MainActivity:
using Android.App;
using Android.Widget;
using Android.OS;
using System;
using System.Collections.Generic;
using Android.Support.V7.Widget;
using Android.Views;
using Android.Content;
namespace RecycleTest
{
[Activity(Label = "RecycleTest", MainLauncher = true)]
public class MainActivity : Activity
{
RecyclerView mRecyclerView;
MyAdapter adapter;
Button bt1, bt2, bt3;
List<double> list1, list2, list3;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
initView();
initData();
mRecyclerView = FindViewById<RecyclerView>(Resource.Id.rv);
mRecyclerView.SetLayoutManager(new LinearLayoutManager(this));
adapter = new MyAdapter(this, list1);
mRecyclerView.SetAdapter(adapter);
}
private void initData()
{
list1 = new List<double>();
list2 = new List<double>();
list3 = new List<double>();
list1.Add(1.1);
list1.Add(1.2);
list1.Add(1.3);
list2.Add(2.1);
list2.Add(2.2);
list2.Add(2.3);
list2.Add(2.4);
list3.Add(3.1);
list3.Add(3.2);
list3.Add(3.3);
list3.Add(3.4);
list3.Add(3.5);
}
public void initView() {
bt1 = FindViewById<Button>(Resource.Id.bt1);
bt2 = FindViewById<Button>(Resource.Id.bt2);
bt3 = FindViewById<Button>(Resource.Id.bt3);
bt1.Click += Bt1_Click;
bt2.Click += Bt2_Click;
bt3.Click += Bt3_Click;
}
private void Bt3_Click(object sender, System.EventArgs e)
{
adapter.change(list3);
}
private void Bt2_Click(object sender, System.EventArgs e)
{
adapter.change(list2);
}
private void Bt1_Click(object sender, System.EventArgs e)
{
adapter.change(list1);
}
}
class MyViewHolder : RecyclerView.ViewHolder
{
public TextView mtv;
public MyViewHolder(View itemView): base(itemView)
{
mtv = itemView.FindViewById<TextView>(Resource.Id.item_tv);
}
}
class MyFootViewHolder : RecyclerView.ViewHolder
{
public TextView mtv;
public MyFootViewHolder(View itemView):base(itemView)
{
mtv = itemView.FindViewById<TextView>(Resource.Id.foot_tv);
}
}
class MyAdapter :RecyclerView.Adapter{
Context mContext;
List<double> mList=new List<double>();
public override int ItemCount => mList.Count + 1;
public MyAdapter(Context context, List<Double> list)
{
this.mContext = context;
this.mList.AddRange(list);
}
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
if (GetItemViewType(position) == 0)
{
((MyViewHolder)holder).mtv.Text=(mList[position].ToString());
}
else
{
double price = 0;
for (int i = 0; i < mList.Count; i++)
{
price += mList[i];
}
((MyFootViewHolder)holder).mtv.Text=price + "";
}
}
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
{
RecyclerView.ViewHolder holder;
if (viewType == 0)
{
holder = new MyViewHolder(LayoutInflater.From(mContext).Inflate(Resource.Layout.item_layout, parent, false));
}
else
{
holder = new MyFootViewHolder(LayoutInflater.From(mContext).Inflate(Resource.Layout.foot_layout, parent, false));
}
return holder;
}
public override int GetItemViewType(int position)
{
int type;
if (position != mList.Count)
{
type = 0;
}
else
{
type = 1;
}
return type;
}
public void change(List<Double> list1)
{
mList.Clear();
mList.AddRange(list1);
NotifyDataSetChanged();
}
}
}
I am populating a firebase data with firebase recycler adapter through databinding and want to populate images with the data either from Picasso or Glide, but unable to do. So can any one help me what to write and where to write the code of either Picasso or Glide.
public class ShopByCategoryFragment extends Fragment {
FirebaseRecyclerAdapter adapter;
Firebase mFirebaseRef = new Firebase("https://abc.firebaseio.com/").child("subCategories");
public ShopByCategoryFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.recycler_view, container, false);
final RecyclerView recyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);
adapter = new FirebaseRecyclerAdapter<SubCategories, ViewHolder>(SubCategories.class, R.layout.fragment_shop_by_category,
ViewHolder.class, mFirebaseRef) {
#Override
protected void populateViewHolder(ViewHolder viewHolder, SubCategories subCategories, int i) {
FragmentShopByCategoryBinding binding = viewHolder.getBinding();
binding.setSubCategories(subCategories);
}
};
recyclerView.setAdapter(adapter);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 2));
return rootView;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public FragmentShopByCategoryBinding binding;
public ViewHolder(View itemView) {
super(itemView);
binding = DataBindingUtil.bind(itemView);
}
public FragmentShopByCategoryBinding getBinding() {
return binding;
}
}
#Override
public void onDestroy() {
super.onDestroy();
adapter.cleanup();
}
}
<layout xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="SubCategories"
type="com.abc.www.shopping.model.SubCategories"/>
</data>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="wrap_content"
android:layout_height="#dimen/tile_height"
android:id="#+id/sub_category_image"
android:scaleType="centerCrop"
android:src="#{SubCategories.image}" />
<TextView
android:layout_width="wrap_content"
android:layout_height="#dimen/tile_height"
android:text="#{SubCategories.title}"
android:id="#+id/sub_category_title"
android:padding="16dp"
android:textColor="#color/colorSubCategoryTitle"
android:layout_gravity="center"
android:textAppearance="#android:style/TextAppearance.Material.Title"
tools:targetApi="lollipop" />
</FrameLayout>
</android.support.v7.widget.CardView>
public class SubCategories extends BaseObservable {
private String title;
private String image;
public SubCategories() {
// empty constructor
}
public SubCategories(String title, String image) {
this.title = title;
this.image = image;
}
public String getTitle() {
return title;
}
public String getImage() {
return image;
}
}
I have made something similar to what you need. I'll share my code and do a little explanation.
I have a class that returns the FirebaseRecyclerAdapter like this:
public RecyclerView.Adapter postAdapter(String userKey, final Context context, MyClickListener clickListener) {
Query userPostRef = postRef.child(userKey);
myClickListener = clickListener;
return new FirebaseRecyclerAdapter<PostModel, DataObjectHolder>(PostModel.class, R.layout.stream_layout, DataObjectHolder.class, userPostRef) {
#Override
public void populateViewHolder(final DataObjectHolder dataviewHolder, final PostModel postModel, int position) {
StorageReference mStorageRef = FirebaseStorage.getInstance().getReference();
mStorageRef.child("uploaded_captures/" + postModel.getImageFilename() + ".jpg").getDownloadUrl()
.addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
dataviewHolder.setImage(uri.toString(), context);
dataviewHolder.setComment(postModel.getPostComment());
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception exception) {
Log.e("FirebaseRecyclerAdapter", exception.getMessage());
}
});
}
};
}
and a ViewHolder that loads the image into the ImageView used by the layout:
private static class DataObjectHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
View mView;
public DataObjectHolder(View itemView) {
super(itemView);
mView = itemView;
}
void setImage(final String imageURL, final Context context) {
final ImageView postImage = (ImageView) mView.findViewById(R.id.imageView);
if (BuildConfig.DEBUG) {
Picasso.with(context).setIndicatorsEnabled(true);
}
Picasso.with(context)
.load(imageURL)
.networkPolicy(NetworkPolicy.OFFLINE)
.placeholder(R.drawable.ic_menu_camera)
.fit().centerCrop().into(postImage, new Callback() {
#Override
public void onSuccess() {
}
#Override
public void onError() {
Picasso.with(context)
.load(imageURL)
.placeholder(R.drawable.ic_menu_camera)
.fit().centerCrop().into(postImage);
}
});
}
void setComment(String text) {
TextView comment = (TextView) mView.findViewById(R.id.comment);
comment.setText(text);
}
#Override
public void onClick(View v) {
myClickListener.onItemClick(getAdapterPosition(), v);
}
}
On Picasso I'm using the NerworkPolicy.OFFLINE to check if the image is on the disk cache before trying to get it from the network. It's not perfect and I'm still having some issues with the delay of the images once the ViewHolder it's been recycled but I think it's a start for you to go on.
In my ViewHolder I do this by:
Glide.with(context)
.load(chat.getImageUrl())
.into(imageView);
I haven't tried using data binding for this yet, but the code is simple enough.
I'm currently creating an app for Android using MvvmCross. Part of that app requires a MvxListView, where each item has 2 TextViews. One of these TextViews are hidden by default. I'm looking to implement an accordion like functionality, where clicking on the first TextView will show/hide the other TextView.
I've currently gotten most of this to work with the MvvmCross Visibility Plugin, but the click event is bound to the MvxListView instead of the TextView inside it. What I've currently gotten to work looks like this:
FirstViewModel:
public class FirstViewModel
: MvxViewModel
{
public FirstViewModel(IListService listService)
{
Interests = new ObservableCollection<Interest>();
List<Interest> tempInterests = listService.GetInterestFeeds("");
foreach (var interest in tempInterests)
{
interest._parent = this;
Interests.Add(interest);
}
var pluginLoader = new PluginLoader();
pluginLoader.EnsureLoaded();
}
private ObservableCollection<Interest> _interests;
public ObservableCollection<Interest> Interests
{
get { return _interests; }
set { _interests = value; RaisePropertyChanged(() => Interests); }
}
public ICommand ItemVisibleCommand
{
get
{
return new MvxCommand<Interest>(item => item.IsVisible = !item.IsVisible);
}
}
}
FirstView:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
...>
<Mvx.MvxListView
...
local:MvxBind="ItemsSource Interests"
local:MvxItemTemplate="#layout/item_interests" />
</LinearLayout>
item_interests:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="30dp"
local:MvxBind="Text InterestName" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="20dp"
local:MvxBind="Text InterestDescription; Visibility IsVisible, Converter=Visibility" />
</LinearLayout>
In order to bind it to the TextView inside the MvxListView, I've been trying to modify my code to something similar to How to bind ItemClick in MvxListView in MvxListView as per the anwser by Stuart, resulting in the following code:
item_interest:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="30dp"
local:MvxBind="Text InterestName; Click ItemVisibleCommand" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="20dp"
local:MvxBind="Text InterestDescription; Visibility IsVisible, Converter=Visibility" />
</LinearLayout>
FirstViewModel:
public class FirstViewModel
: MvxViewModel
{
public FirstViewModel(IListService listService)
{
Interests = new ObservableCollection<Interest>();
List<Interest> tempInterests = listService.GetInterestFeeds("");
foreach (var interest in tempInterests)
{
interest._parent = this;
Interests.Add(interest);
}
var pluginLoader = new PluginLoader();
pluginLoader.EnsureLoaded();
}
private ObservableCollection<Interest> _interests;
public ObservableCollection<Interest> Interests
{
get { return _interests; }
set { _interests = value; RaisePropertyChanged(() => Interests); }
}
public void MakeItemVisible(bool isVisible)
{
isVisible = !isVisible;
}
Interest:
public class Interest : INotifyPropertyChanged
{
public string InterestId { get; set; }
public string InterestName { get; set; }
public string InterestDescription { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public FirstViewModel _parent { get; set; }
private bool _isVisible;
public bool IsVisible
{
get { return _isVisible; }
set
{
_isVisible = value;
onPropertyChanged(this, "IsVisible");
}
}
private void onPropertyChanged(object sender, string propertyName)
{
if (this.PropertyChanged != null)
{
PropertyChanged(sender, new PropertyChangedEventArgs(propertyName));
}
}
public Interest(string id, string name, string description)
{
//Initialisers
}
private MvxCommand<bool> _itemVisible;
public ICommand ItemVisibleCommand
{
get
{
_itemVisible = _itemVisible ?? new MvxCommand<bool>(IsVisible => _parent.MakeItemVisible(IsVisible));
return _itemVisible;
}
}
}
resulting in the following exception:
04-04 15:05:40.575 I/MonoDroid(18011): UNHANDLED EXCEPTION: System.NullReferenceException: Object reference not set to an instance of an object
04-04 15:05:40.575 I/MonoDroid(18011): at Cirrious.MvvmCross.ViewModels.MvxCommand`1<bool>.Execute (object) <IL 0x00010, 0x00088>
04-04 15:05:40.575 I/MonoDroid(18011): at Cirrious.MvvmCross.Binding.Droid.Target.MvxViewClickBinding.ViewOnClick (object,System.EventArgs) <IL 0x0001f, 0x000fb>
04-04 15:05:40.575 I/MonoDroid(18011): at Android.Views.View/IOnClickListenerImplementor.OnClick (Android.Views.View) [0x0000d] in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.12-series/a1e3982a/source/monodroid/src/Mono.Android/platforms/android-15/src/generated/Android.Views.View.cs:1615
04-04 15:05:40.575 I/MonoDroid(18011): at Android.Views.View/IOnClickListenerInvoker.n_OnClick_Landroid_view_View_ (intptr,intptr,intptr) [0x00011] in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.12-series/a1e3982a/source/monodroid/src/Mono.Android/platforms/android-15/src/generated/Android.Views.View.cs:1582
04-04 15:05:40.575 I/MonoDroid(18011): at (wrapper dynamic-method) object.a963c1ac-b573-4022-b41d-f0f002438c84 (intptr,intptr,intptr) <IL 0x00017, 0x00043>
Unhandled Exception:
System.NullReferenceException: Object reference not set to an instance of an object
Thanks in advance to anyone who's taken the time to read all that :)
UPDATE - I tried to do as Stuart suggested, and got the following solution:
First off, to preserve the original Interest entity, it got wrapped in an InterestWrapper.
public class InterestWrapper : INotifyPropertyChanged
{
private Interest _interest;
private InterestAndroidViewModel _parent; //TO-DO
public Interest Item { get { return _interest; } }
public event PropertyChangedEventHandler PropertyChanged;
private bool _isVisible;
public bool IsVisible
{
get { return _isVisible; }
set
{
_isVisible = value;
onPropertyChanged(this, "IsVisible");
}
}
private void onPropertyChanged(object sender, string propertyName)
{
if (this.PropertyChanged != null)
{
PropertyChanged(sender, new PropertyChangedEventArgs(propertyName));
}
}
public InterestWrapper(Interest interest, InterestAndroidViewModel parent)
{
IsVisible = false;
_interest = interest;
_parent = parent;
}
public IMvxCommand ItemVisibleCommand
{
get
{
return new MvxCommand(() => _parent.MakeItemVisible(_interest));
}
}
}
FirstViewModel
public class FirstViewModel
: MvxViewModel
{
public FirstViewModel(IListService listService)
{
Interests = new ObservableCollection<InterestWrapper>();
List<Interest> tempInterests = listService.GetInterestFeeds("");
foreach (var interest in tempInterests)
{
InterestWrapper wrapper = new InterestWrapper(interest, this);
Interests.Add(wrapper);
}
}
private ObservableCollection<InterestWrapper> _interests;
public ObservableCollection<InterestWrapper> Interests
{
get { return _interests; }
set { _interests = value; RaisePropertyChanged(() => Interests); }
}
public void MakeItemVisible(Interest interest)
{
if (interest.IsVisible)
{
interest.IsVisible = !interest.IsVisible;
}
else
{
foreach (var _interest in _interests)
{
_interest.Item.IsVisible = false;
}
interest.IsVisible = !interest.IsVisible;
}
}
}
item_interest:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
...
<RelativeLayout
...
<TextView
...
local:MvxBind="Text Item.InterestName; Click ItemVisibleCommand" />
<Mvx.MvxImageView
...
local:MvxBind="Visibility Item.IsVisible, Converter=InvertedVisibility; Click ShowEducationsCommand" />
</RelativeLayout>
</RelativeLayout>
The local:MvxBind="Text InterestName; Click ItemVisibleCommand" can only really call a non-parameterized MvxCommand - it can't call MvxCommand<bool> as it doesn't know what the bool value is.
If you wanted to, you could use the CommandParameter converter to pass in the value - e.g. local:MvxBind="Text InterestName; Click CommandParameter(ItemVisibleCommand, IsVisible)"
But overall, in this case I'd probably recommend rewriting ItemVisibleCommand as just a "toggle visible" command instead
Look through this question it's the as your one and has the solution suggested by Stuart, so think it's what you are looking for.
Binding button click in ListView template MvvMCross