How to underline TextView in xml (Xamarin.Android)? - xamarin.android

I have to underline the TextViews of the following table:
The only thing I found in internet is:
http://www.apnatutorials.com/android/how-to-underline-text-in-android.php?categoryId=2 (Approach 4)
But the value of the text in that case is hardcoded. In my case there are 9 rows which can change its number. So hardcoding is not solution for me.

How to underline TextView in xml (Xamarin.Android)?
You could create a custom TextView and use it in your .axml file.
Create an underline TextView class:
public class UnderlineTextView : TextView
{
public UnderlineTextView(Context context, IAttributeSet attrs) :
base(context, attrs)
{
Initialize();
}
public UnderlineTextView(Context context, IAttributeSet attrs, int defStyle) :
base(context, attrs, defStyle)
{
Initialize();
}
private void Initialize()
{
//this.PaintFlags = this.PaintFlags | Android.Graphics.PaintFlags.UnderlineText;
//this.Text = "This text will be underlined";
String underlineData = this.Text;
SpannableString content = new SpannableString(underlineData);
content.SetSpan(new UnderlineSpan(), 0, underlineData.Length, 0);
// 0 specify start index and underlineData.length() specify end index of styling
this.TextFormatted = content;
}
}
Then, use it in axml:
<yournamespace.UnderlineTextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="I am underline TextView"/>

Related

Youtube Video is not loaded in WebView

I want to display a list of Youtube Video using ListView and WebView. But Videos are not loaded.
WebView always shows An Error Occured as the picture below:
Currently I don't know how to bind URL directly to WebView in AXML file, therefor I customized a BindableWebView instead:
public class BindableWebView : WebView
{
public BindableWebView(Context context, IAttributeSet attrs)
: base(context, attrs)
{
}
private string _webViewContent;
public string WebViewContent
{
get { return _webViewContent; }
set
{
_webViewContent = value;
LoadHtmlString();
}
}
private void LoadHtmlString()
{
LoadData(WebViewContent, "text/html", "utf-8");
}
}
This is Layout AXML file:
<dc.AlphaLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
local:MvxBind="Click ItemClickCommand">
<!--Video Thumbnail-->
<FrameLayout
android:layout_width="#dimen/ProductImageWidth"
android:layout_height="#dimen/ProductImageHeight"
android:layout_margin="#dimen/ExtraNewsImagePadding">
<BindableWebView
android:id="#+id/playerWebView"
android:layout_width="match_parent"
android:layout_height="match_parent"
local:MvxBind="WebViewContent Video.YoutubeUrl" />
</FrameLayout>
<LinearLayout
.....
</LinearLayout>
</dc.AlphaLinearLayout>
This is Video Model:
namespace ....Shared.Models
{
public class Video
{
public string YoutubeId { get; set; }
public string YoutubeImageUrl => $"https://img.youtube.com/vi/{YoutubeId}/0.jpg";
public string YoutubeUrl => $"<iframe width=\"100%\" height=\"100%\" src=\"https://www.youtube.com/embed/{YoutubeId}\" frameborder=\"0\" allowfullscreen></iframe>";
public Guid NewsId { get; set; }
}
}
And VideoListViewm in C#:
namespace DeHeus.Droid.Views
{
public class VideoListView : DetailViewWithShare
{
protected override int LayoutId => Resource.Layout.VideoListView;
private CustomMvxListView _videoListview;
protected override void InitView(View view)
{
_videoListview = view.FindViewById<CustomMvxListView>(Resource.Id.videoList);
_videoListview.ItemTemplateId = Resource.Layout.VideoListItemView;
}
protected override void CreateBinding()
{
var bindingSet = this.CreateBindingSet<VideoListView, VideoListViewModel>();
bindingSet.Bind(_videoListview.Adapter)
.For(v => v.ItemsSource)
.To(vm => vm.VideoItemViewModels);
bindingSet.Bind(_videoListview)
.For(v => v.ScrollToBottom)
.To(vm => vm.ScrollToBottomCommand);
bindingSet.Apply();
}
}
}
Running debug, the WebViewContent gets exact Url but I don't know why it doesn't work as I thought: "<iframe width=\"100%\" height=\"100%\" src=\"https://www.youtube.com/embed/usoFYAqOMyA\" frameborder=\"0\" allowfullscreen></iframe>"
Does anyone has idea?
Thanks
Done a lot of researching and I have finally solved the issue.
All you need is putting SetWebChromeClient(new WebChromeClient()); before LoadData
public BindableWebView(Context context, IAttributeSet attrs)
: base(context, attrs)
{
WebSettings settings = this.Settings;
settings.JavaScriptEnabled = true;
SetWebChromeClient(new WebChromeClient());
}
private string _webViewContent;
public string WebViewContent
{
get { return _webViewContent; }
set
{
_webViewContent = value;
LoadHtmlString();
}
}
private void LoadHtmlString()
{
LoadData(WebViewContent, "text/html", "utf-8");
}

AppBarLayout CollapsingToolbar how to disable expand on EditText click?

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
}

Multi Select Spinner Xamarin Android

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);
}
}

Right-align column contents in Vaadin Grid?

In the new Vaadin Grid widget (alternative to venerable Table), how does one right-align numbers or other content in a column?
The simplest way I can think of is to define your own CSS classes and style generator, pretty much similar to what I'd had done when working with tables.
#Theme("mytheme")
#Widgetset("com.matritza.MyAppWidgetset")
public class MyUI extends UI {
#WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
#VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
public static class MyUIServlet extends VaadinServlet {
// meh, default stuff
}
#Override
protected void init(VaadinRequest vaadinRequest) {
final VerticalLayout layout = new VerticalLayout();
layout.setMargin(true);
setContent(layout);
// create a grid
Grid grid = new Grid("Grid test");
// create a specific container for the grid to hold our persons
BeanItemContainer<Person> container = new BeanItemContainer<>(Person.class);
grid.setContainerDataSource(container);
// define our own style generator
grid.setCellStyleGenerator(new Grid.CellStyleGenerator() {
#Override
public String getStyle(Grid.CellReference cellReference) {
if ("age".equals(cellReference.getPropertyId())) {
// when the current cell is number such as age, align text to right
return "rightAligned";
} else {
// otherwise, align text to left
return "leftAligned";
}
}
});
// generate some dummy data
for (int i = 0; i < 10; i++) {
container.addItem(new Person("Name " + i, "Surname " + i, i));
}
layout.addComponent(grid);
}
// basic class to populate the grid in a fast & simple way
public class Person {
private String name;
private String surname;
private int age;
private Person(String name, String surname, int age) {
this.name = name;
this.surname = surname;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
}
And the basic CSS styles
#mixin mytheme {
#include valo;
// Insert your own theme rules here
.leftAligned {
text-align: left;
}
.rightAligned {
text-align: right;
}
}
And you should see something like
By the way, in Java 8 and later, the new Lambda syntax for that style generator would be:
grid.setCellStyleGenerator(( Grid.CellReference cellReference ) -> {
if ( "age".equals( cellReference.getPropertyId() ) ) {
// when the current cell is number such as age, align text to right
return "rightAligned";
} else {
// otherwise, align text to left
return "leftAligned";
}
});
One can also use already present styles like v-align-right, v-align-middle, etc. Just see what themes like Valo already contain, and extend existing themes only when needed.
Here's simple example how one could implement cell generator with regexp (matching one or multiple fields based on name of field)
public class RegexpCellStyleGenerator implements CellStyleGenerator {
private String regex = ".*"; // defaults all
String style = "v-align-right"; // default is here just as example
// special version useful only when one wants to style all fields inside grid
public RegexpCellStyleGenerator(String style) {
super();
this.style = style;
}
public RegexpCellStyleGenerator(String regex, String style) {
super();
this.regex = regex;
this.style = style;
}
#Override
public String getStyle(CellReference cellReference) {
String propertyId = cellReference.getPropertyId().toString();
if (propertyId.matches(regex)) {
return style;
}
return null;
}
and as this is only partially useful as most grids have multiple fields composite generator could be handy
public class CompositeCellStyleGenerator implements CellStyleGenerator {
List<CellStyleGenerator> generators = new ArrayList<>();
public CompositeCellStyleGenerator() {}
public void addCellStyleGenerator(CellStyleGenerator generator) {
generators.add(generator);
}
#Override
public String getStyle(CellReference cellReference) {
List<String> styles = new ArrayList<>();
for (CellStyleGenerator generator : generators) {
String style = generator.getStyle(cellReference);
if (style != null) {
styles.add(style);
}
}
if (!styles.isEmpty()) {
return styles.stream().collect(Collectors.joining(" "));
}
return null;
}
Composite generator joins all styles together and can be used like this. If there's multiple styles for one column both are applied.
RegexpCellStyleGenerator yearGenerator = new RegexpCellStyleGenerator("yearOfFoundation", "v-align-right");
RegexpCellStyleGenerator nameGenerator = new RegexpCellStyleGenerator("name", "v-align-center");
RegexpCellStyleGenerator nameGenerator2 = new RegexpCellStyleGenerator("name", "v-label-huge");
CompositeCellStyleGenerator compositeGenerator = new CompositeCellStyleGenerator();
compositeGenerator.addCellStyleGenerator(yearGenerator);
compositeGenerator.addCellStyleGenerator(nameGenerator);
compositeGenerator.addCellStyleGenerator(nameGenerator2);
grid.setCellStyleGenerator(compositeGenerator);
Note that composite generator can use generic generators like one with regexp definitions and more complex use case specific ones.
Hope this helps those who try to find easy way to style cells. Happy Experimenting.

Spinner Control in Monodroid - Get ID and Text on Selection

I have started exploring the Spinner control just now. I have pretty much achieved what I wanted but only the last step is missing. Here is what I have done so far.
I have a very simple class for this example:
[Serializable]
public class Merchant
{
public Int64 MerchantId { get; set; }
public String ShopName { get; set; }
public override string ToString()
{
return ShopName;
}
}
Here is the axml where I placed the Spinner:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="1280dip"
android:layout_height="800dip">
<TextView
android:text="Select a merchant:"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/lblSelect"
android:layout_marginLeft="30dip"
android:layout_marginTop="30dip"
android:textSize="42dip" />
<Spinner
android:id="#+id/spinMerchant"
android:layout_width="1000dip"
android:layout_height="wrap_content"
android:layout_below="#id/lblSelect"
android:prompt="#string/spinner_prompt"
android:layout_centerHorizontal="true"
android:minHeight="20dip" />
</RelativeLayout>
And my code is:
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
// Create your application here
this.SetContentView(Resource.Layout.MerchantSelect);
List<Merchant> lstMerchant = new List<Merchant> ();
lstMerchant.Add (new Merchant() { ShopName = "First Shop", MerchantId = 11 });
lstMerchant.Add (new Merchant() { ShopName = "Second Shop", MerchantId = 12 });
Spinner spinner = this.FindViewById<Spinner>(Resource.Id.spinMerchant);
ArrayAdapter adapter = new ArrayAdapter (this, Android.Resource.Layout.SimpleSpinnerItem, lstMerchant);
spinner.ItemSelected += new EventHandler<AdapterView.ItemSelectedEventArgs> (spinner_ItemSelected);
spinner.Adapter = adapter;
}
private void spinner_ItemSelected (object sender, AdapterView.ItemSelectedEventArgs e)
{
Spinner spinner = (Spinner)sender;
//Merchant merch = (Merchant)spinner.SelectedItem;
string toast = string.Format ("Selected text is {0}", spinner.GetItemAtPosition (e.Position));
Toast.MakeText (this, toast, ToastLength.Long).Show ();
}
I want to get the selected text as well as the ID behind it as soon as the selection is made. I get the text by spinner.GetItemAtPosition (e.Position) but I can't seem to find anything that can give me the ID. If I try to do Merchant merch = (Merchant)spinner.SelectedItem; I get an exception: Cannot convert type 'Java.Lang.Object' to 'Merchant'.
Please let me know how it can be achieved.
Thanks.
The spinner only knows about the text because that's what you have in your ToString() method it. The adapter that's providing the spinner data has no idea it is dealing with Merchant objects.
To get to the actual Merchant object, youll have to make your List<Merchant> lstMerchant a member of your class.
Then change your selection handler:
private void spinner_ItemSelected (object sender, AdapterView.ItemSelectedEventArgs e)
{
Spinner spinner = (Spinner)sender;
// Get the ID from your model.
Merchant merch = this.lstMerchant[e.Position];
var id = merch.MerchantId;
string toast = string.Format ("Selected text is {0}", spinner.GetItemAtPosition (e.Position));
Toast.MakeText (this, toast, ToastLength.Long).Show ();
}

Resources