Pixels To Xamarin.Forms Font Size - xamarin.forms

i am using Xamarin forms and i have a project that requires the font size to be 32 pixels. I kept searching on google however i could not find an answer on how to convert this pixels in Xamarin device-independent units. I would greatly appreciate some help.
Thank you.

Xamarin uses DIU (device-independent units) about everywhere.
Have a look at https://learn.microsoft.com/en-us/xamarin/xamarin-forms/creating-mobile-apps-xamarin-forms/summaries/chapter05
I had a similiar issue and found out via trial and error that Xamarin.Essentials.DeviceDisplay.MainDisplayInfo.Density returns the amount of pixels per DIU on the device (tested on iPod Touch, Nokia 8 and Firetablet).
So this should be correct:
myLabel.FontSize = 32 / Xamarin.Essentials.DeviceDisplay.MainDisplayInfo.Density;
But as Jason has stated, you might want to check your requirements, because the more pixels are on the device, smaller the font gets. Thats why people use DIU in the first place :-)
It is probably better to have a look at how big the font is on the customers homepage. Maybe measure it there in inches, centimeters or whatever fits you and then do the math to convert that to DIUs (see link above, 64 DIU = 1 centimeter).

You could use custom renderer to invoke native method to set 32 pixels.Based on Button control, you can create a custom Button in Xamaarin forms as follows:
public class MyButton : Button
{
}
Then Andoird solution create the custom rendere class as follows:
[assembly: ExportRenderer(typeof(Xamarin.Forms.MyButton), typeof(CustomButtonRenderer))]
namespace AppShellTest.Droid
{
[Obsolete]
public class CustomButtonRenderer: ButtonRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
{
base.OnElementChanged(e);
if (Control != null)
{
// do whatever you want to the UITextField here!
Control.SetTextSize(Android.Util.ComplexUnitType.Px, 32);
}
}
}
}
About other controls, also can create their custom renderers to do.
=================================Update===============================
The method that can convert dp to px or px to dp, you can check that whether it works for you.
public int dip2px(Context context, float dpValue)
{
float scale = context.Resources.DisplayMetrics.Density;
return (int)(dpValue * scale + 0.5f);
}
public int px2dip(Context context, float pxValue)
{
float scale = context.Resources.DisplayMetrics.Density;
return (int)(pxValue / scale + 0.5f);
}
Then renderer code will be:
public class CustomButtonRenderer: ButtonRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
{
base.OnElementChanged(e);
if (Control != null)
{
// do whatever you want to the UITextField here!
Control.SetTextSize(Android.Util.ComplexUnitType.Px, dip2px(32));
}
}
public int dip2px(float dpValue)
{
float scale = MainActivity.instance.Resources.DisplayMetrics.Density;
return (int)(dpValue * scale + 0.5f);
}
}
The MainActivity.instance declared in MainActivity that means the instance of MainActivity.
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
public static MainActivity instance { set; get; }
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
instance = this;
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
}
...

Related

Unable To Retrieve Custom Control Value from View

Xamarin Forms Android Autosize Label TextCompat pre android 8 doesn't autosize text
I unfortunately do not have a high enough rep to comment on anyones post.
I was trying some things out and came across the post linked which got me very close to the solution after experimenting with other posts. I am also trying to autosize text within an app, but inside of an MVVM Master Detail project. If I enter values directly in the Droid renderer it works as expected, but that defeats the purpose when I have fonts of all sizes needed.
I have already made sure my return type is correct.
The code behind is initialized prior to the get value.
The fields are public.
There are no other issues by plugging in numeric values instead of bindable properties.
I am not receiving any values from the view. I would assume the view has not been created yet but the code behind has initialized. I am pretty sure I have done everything mostly right but I mostly deal with stock Xamarin so expanding functionality is still pretty new to me. All help is appreciated.
Custom Control (edit: changed default value from default(int) to an integer value to get rid of exception)
/// <summary>Auto scale label font size class.</summary>
public class AutoSizeLabel : Label
{
/// <summary>Minimum font size property.</summary>
public static readonly BindableProperty MinimumFontSizeProperty = BindableProperty.Create(
propertyName: nameof(MinimumFontSize),
returnType: typeof(int),
declaringType: typeof(AutoSizeLabel),
defaultValue: 17);
/// <summary>Maximum font size property.</summary>
public static readonly BindableProperty MaximumFontSizeProperty = BindableProperty.Create(
propertyName: nameof(MaximumFontSize),
returnType: typeof(int),
declaringType: typeof(AutoSizeLabel),
defaultValue: 24);
/// <summary>Gets or sets minimum font size.</summary>
public int MinimumFontSize
{
get
{
return (int)this.GetValue(MinimumFontSizeProperty);
}
set
{
this.SetValue(MinimumFontSizeProperty, value);
}
}
/// <summary>Gets or sets maximum font size.</summary>
public int MaximumFontSize
{
get
{
return (int)this.GetValue(MaximumFontSizeProperty);
}
set
{
this.SetValue(MaximumFontSizeProperty, value);
}
}
}
Droid Renderer
public class AutoSizeLabelRenderer : LabelRenderer
{
protected override bool ManageNativeControlLifetime => false;
protected override void Dispose(bool disposing)
{
Control.RemoveFromParent();
base.Dispose(disposing);
}
private AutoSizeLabel bindingValue = new AutoSizeLabel();
private AppCompatTextView appCompatTextView;
public AutoSizeLabelRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if (e.NewElement == null || !(e.NewElement is AutoSizeLabel autoLabel) || Control == null) { return; }
//v8 and above supported natively, no need for the extra stuff below.
if (DeviceInfo.Version.Major >= 8)
{
Control?.SetAutoSizeTextTypeUniformWithConfiguration(bindingValue.MinimumFontSize, bindingValue.MaximumFontSize, 2, (int)ComplexUnitType.Sp);
return;
}
appCompatTextView = new AppCompatTextView(Context);
appCompatTextView.SetTextColor(Element.TextColor.ToAndroid());
appCompatTextView.SetMaxLines(1);
appCompatTextView.SetBindingContext(autoLabel.BindingContext);SetNativeControl(appCompatTextView);
TextViewCompat.SetAutoSizeTextTypeUniformWithConfiguration(Control, bindingValue.MinimumFontSize, bindingValue.MaximumFontSize, 2, (int)ComplexUnitType.Sp);
}
}
XAML Call
<renderer:AutoSizeLabel MinimumFontSize="17"
MaximumFontSize="24"
Style="{StaticResource SomeStyle}"
Text="{Binding SomeText}">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding SomeCommand}"></TapGestureRecognizer>
</Label.GestureRecognizers>
</renderer:AutoSizeLabel>
This line is unnecessary.
private AutoSizeLabel bindingValue = new AutoSizeLabel();
Instead reference autoLabel. Alternatively I changed the check to
if (e.NewElement == null || Control == null) { return; }
and cast in the following line using
var autoSizeLabel = e.NewElement as AutoSizeLabel;

Xamarin forms: How to increase the distance between keyboard and editor

I need to increase the distance between the soft-keyboard and the editor in the UI. Currently, the keyboard overlaps the editor bottom part.
Screenshot adding below:
Is there any solution for this?
Try the below code it will be help you.
In Xamarin Android Project
public class MainActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
Window.SetSoftInputMode(Android.Views.SoftInput.AdjustUnspecified);
}
}
or in AndroidManifest.xml
<activity android:name=".myActivity"
android:label="#string/app_name"
android:screenOrientation="sensorPortrait"
android:windowSoftInputMode="adjustPan"/>
check the java code on given link- KeyboardSize
Xamarin forms: How to increase the distance between keyboard and editor
You could implement this feature by add paddingBottom property for your Edior.
Here is an example, add the paddingBottom property in your EditorRenderer:
public class CustomEditorRenderer : EditorRenderer
{
public CustomEditorRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Editor> e)
{
base.OnElementChanged(e);
if (Control == null)
return;
//Control.Background = new ColorDrawable(Android.Graphics.Color.Transparent);
//Control.Background = null;
float scale = Context.Resources.DisplayMetrics.Density;
int dpAsPixels = (int)(50 * scale + 0.5f);
Control.SetPadding(0, 0, 0, dpAsPixels);
}
}
Effect: Original space, Custom space.

How to make always visible headers of rows in RowsFragment

Here is what i have now.
Simply my RowsFragment is look like this,
public static class SampleFragmentB extends RowsFragment {
private final ArrayObjectAdapter mRowsAdapter;
public SampleFragmentB() {
mRowsAdapter = new ArrayObjectAdapter(new ShadowRowPresenterSelector());
setAdapter(mRowsAdapter);
setOnItemViewClickedListener(new OnItemViewClickedListener() {
#Override
public void onItemClicked(
Presenter.ViewHolder itemViewHolder,
Object item,
RowPresenter.ViewHolder rowViewHolder,
Row row) {
Toast.makeText(getActivity(), "Implement click handler", Toast.LENGTH_SHORT)
.show();
}
});
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
createRows();
getMainFragmentAdapter().getFragmentHost().notifyDataReady(getMainFragmentAdapter());
}
private void createRows() {
String json = Utils.inputStreamToString(getResources().openRawResource(
R.raw.page_row_example));
CardRow[] rows = new Gson().fromJson(json, CardRow[].class);
for (CardRow row : rows) {
mRowsAdapter.add(createCardRow(row));
}
}
private Row createCardRow(CardRow cardRow) {
PresenterSelector presenterSelector = new CardPresenterSelector(getActivity());
ArrayObjectAdapter adapter = new ArrayObjectAdapter(presenterSelector);
for (Card card : cardRow.getCards()) {
adapter.add(card);
}
HeaderItem headerItem = new HeaderItem(cardRow.getTitle());
return new CardListRow(headerItem, adapter, cardRow);
}
}
This is what i exactly want to do,
So I want to make always visible Header of each row without selecting into or focusing into RowsFragment. I'am using leanback v24 to add multiple rows into each header item.
You might want to check the Sofa library for Android TV that extends the Leanback library capabilities by offering a set of more powerful features. In BrowseSupportFragment, mRowsFragment.setExpand should be true. Additional reference: how to always show headers in RowsFragment

Fragment already added IllegalStateException in viewpager

I'm using viewpager to display pictures. I just need three fragments basically: previous image to preview, current display image and next image to preview. I would like to just display a preview of previous and next image, it will change to full image when user actually swipe to it. So I'm thinking of just using 3 fragment to achieve this. Code is below:
private class ImagePagerAdapter extends FragmentStatePagerAdapter implements ViewPager.OnPageChangeListener {
private ImageFragment mImageFragment;
private ImagePreviewFragment mPreviousPreviewFragment;
private ImagePreviewFragment mNextPreviewFragment;
public ImagePagerAdapter(FragmentManager fm, ImageFragment image, ImagePreviewFragment previous, ImagePreviewFragment next) {
super(fm);
mImageFragment = image;
mPreviousPreviewFragment = previous;
mNextPreviewFragment = next;
}
#Override
public Fragment getItem(int position) {
if (position == mPager.getCurrentItem()) {
mImageFragment.display(position);
return mImageFragment;
}
if (position < mPager.getCurrentItem()) {
mPreviousPreviewFragment.display(position - 1);
return mPreviousPreviewFragment;
}
mNextPreviewFragment.display(position + 1);
return mNextPreviewFragment;
}
#Override
public int getCount() {
return 100;
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
Log.d(TAG, "onPageScrolled");
}
#Override
public void onPageSelected(final int position) {
Log.d(TAG, "onPageSelected " + position);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
notifyDataSetChanged();
}
}, 500);
}
#Override
public void onPageScrollStateChanged(int state) {
Log.d(TAG, "onPageScrollStateChanged " + state);
}
#Override
public int getItemPosition(Object item) {
return POSITION_NONE;
//return POSITION_UNCHANGED;
}
}
So basically, I pre-created three fragments to display previous/next preview and current image and return them for getItem(). I also notifydatasetchange() in onpageselected() to make all three position to update the fragment when user swipe to new page.
But the problem is that it will throw out
Fragment already added IllegalStateException
when the fragments are added a second time. I think it's because it's been added before. I can create a new fragment every time but I think that's wasteful. So how can I reuse the already created fragment and just update them?
Thanks,
Simon
FragmentStatePagerAdapter design suggests creating a new Fragment for every page (see Google's example). And unfortunately you cannot readd a Fragment once it was added to a FragmentManager (what implicitly happens inside adapter), hence the exception you got. So the official Google-way is to create new fragments and let them be destroyed and recreated by the adapter.
But if you want to reuse pages and utilize an analogue of ViewHolder pattern, you should stick to views instead of fragments. Views could be removed from their parent and reused, unlike fragments. Extend PagerAdapter and implement instantiateItem() like this:
#Override
public Object instantiateItem(ViewGroup container, final int position) {
//determine the view type by position
View view = viewPager.findViewWithTag("your_view_type");
if (view == null) {
Context context = container.getContext();
view = LayoutInflater.from(context).inflate(R.layout.page, null);
view.setTag("your_view_type");
} else {
ViewGroup parent = (ViewGroup) item.getParent();
if (parent != null) {
parent.removeView(item);
}
}
processYourView(position, view);
container.addView(view, MATCH);
return view;
}
You should add some extra logic to determine the view type by position (since you have 3 types of views), I think you can figure that out.

AspectJ capture button clicked

I want to know whether how to capture the button clicked with AspectJ and get its parameter (eg. button name). I think for having more generalized capturing with AspectJ, it shoudl be used MouseListener so it can capture other UI elements in general!
Example:
In a GUI example I have defined 2 buttons that take some actions
public JButton btn1 = new JButton("Test1");
public JButton btn2 = new JButton("Test2");
btn1.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
//take some actions
}
}
btn2.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
//take some actions
}
}
How to capture these buttons with AspectJ, and get their parameters (eg. name)?
It is possible. I have provided two examples. The first that prints out for every JButton that has an ActionListener. The other example only prints out if a specific buttons is clicked.
Prints the text for every JButton clicked with an ActionListener:
#Pointcut("execution(* *.actionPerformed(*)) && args(actionEvent)")
public void buttonPointcut(ActionEvent actionEvent) {}
#Before("buttonPointcut(actionEvent)")
public void beforeButtonPointcut(ActionEvent actionEvent) {
if (actionEvent.getSource() instanceof JButton) {
JButton clickedButton = (JButton) actionEvent.getSource();
System.out.println("Button name: " + clickedButton.getText());
}
}
Prints the text for a specific JButton:
public static JButton j1;
#Pointcut("execution(* *.actionPerformed(*)) && args(actionEvent) && if()")
public static boolean button1Pointcut(ActionEvent actionEvent) {
return (actionEvent.getSource() == j1);
}
#Before("button1Pointcut(actionEvent)")
public void beforeButton1Pointcut(ActionEvent actionEvent) {
// logic before the actionPerformed() method is executed for the j1 button..
}
UPDATED:
You can do this in many different ways. For example add your buttons to the aspect directly. But I prefere to use a enum object between (ButtonManager in this case), so the code does not know about the aspect. And since the ButtonManager is an enum object, it is easy for the aspect to retrieve values from it.
I just tested it with a Swing button class from Oracle and it works. In the Swing class:
b1 = new JButton("Disable middle button", leftButtonIcon);
ButtonManager.addJButton(b1);
AspectJ is extremely powerful when it comes to manipulating classes, but it can not weave advises into specific objects since objects is not created at the time of weaving. So you can only work with objects at runtime and that is why I have added the addJButton(..) method above. That enables the aspect to check the advised button against a list of registered buttons.
The ButtonManager class:
public enum ButtonManager {
;
private static Collection<JButton> buttonList = new LinkedList<JButton>();
public static void addJButton(JButton jButton) {
buttonList.add(jButton);
}
public static Collection<JButton> getButtonList() {
return buttonList;
}
}
Modified pointcut and advice to only print the name of the buttons registered in the ButtonManager:
#Pointcut("execution(* *.actionPerformed(*)) && args(actionEvent) && if()")
public static boolean buttonListPointcut(ActionEvent actionEvent) {
Collection<JButton> buttonList = ButtonManager.getButtonList();
JButton registeredButton = null;
for (JButton jButton : buttonList) {
if (actionEvent.getSource() == jButton) {
registeredButton = jButton;
}
}
return registeredButton != null;
}
#Before("buttonListPointcut(actionEvent)")
public void beforeButtonListPointcut(ActionEvent actionEvent) {
JButton clickedButton = (JButton) actionEvent.getSource();
System.out.println("Registered button name: " + clickedButton.getText());
}
UPDATED 2
Okay, I believe I understand what you want. You want to listen to mouse events. That is possible. The downside is that you have to register all your GUI components that you want to listen for clicks with a mouse listener. It is not enough to register the JPanel of the JFrame with a MouseListener. So if you only have registered an ActionListener for your buttons, you also have to add a mouse listener.
I have created a quick solution that works for me. It only shows that it works. I have not tried to make the solution generic with many different GUI objects. But that should be quite easy to refactor in when you have got the basics to work.
In the Swing class:
private class MouseListener extends MouseInputAdapter {
public void mouseClicked(MouseEvent e) {}
}
In the init method of the Swing class:
MouseListener myListener = new MouseListener();
btn1.addMouseListener(myListener);
btn2.addMouseListener(myListener);
In the Aspect class:
#Pointcut("execution(* *.mouseClicked(*)) && args(mouseEvent)")
public void mouseEventPointcut(MouseEvent mouseEvent) {}
#Before("mouseEventPointcut(mouseEvent)")
public void beforeMouseEventPointcut(MouseEvent mouseEvent) {
if (mouseEvent.getSource() instanceof JButton) {
JButton clickedButton = (JButton) mouseEvent.getSource();
System.out.println("aspectJ --> mouseClicked: " + clickedButton.getText());
}
}
This results in the following output in the console:
aspectJ --> mouseClicked: Test1
I hope it helps!

Resources