I have made a ToolBar, but when I add menu items in menu.xml it always shows as an overflow.
How do I add it separately?
Moreover the Title is shown in the middle (vertically), how do I show it at the top?
menu.xml
<menu 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"
tools:context="com.example.toolbar.MainActivity" >
<item android:id="#+id/action_search"
android:icon="#drawable/ic_launcher"
android:title="#string/action_search"
android:showAsAction="ifRoom"
android:orderInCategory="1"
android:menuCategory="secondary"/>
<item
android:id="#+id/action_settings"
android:orderInCategory="100"
android:title="#string/action_settings"
app:showAsAction="never"/>
</menu>
MainActivity.java
package com.example.toolbar;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.Toolbar;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.my_awesome_toolbar);
setSupportActionBar(toolbar);
// toolbar.setNavigationIcon(R.drawable.back);
toolbar.setLogo(R.drawable.ic_launcher);
//toolbar.setTitle("Title");
// toolbar.setSubtitle("Subtitle");
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.toolbar.MainActivity" >
<android.support.v7.widget.Toolbar
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/my_awesome_toolbar"
android:layout_width="fill_parent"
android:layout_height="128dp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
app:theme="#style/ActionBarThemeOverlay">
</android.support.v7.widget.Toolbar>
</RelativeLayout>
OK, I got the icons because I wrote in menu.xml android:showAsAction="ifRoom" instead of app:showAsAction="ifRoom" since i am using v7 library.
However the title is coming at center of extended toolbar. How to make it appear at the top?
Try to do this:
getSupportActionBar().setDisplayShowTitleEnabled(false);
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
getSupportActionBar().setDisplayShowTitleEnabled(false);
and if you made your custom toolbar (which i presume you did) then you can use the simplest way possible to do this:
toolbarTitle = (TextView)findViewById(R.id.toolbar_title);
toolbarSubTitle = (TextView)findViewById(R.id.toolbar_subtitle);
toolbarTitle.setText("Title");
toolbarSubTitle.setText("Subtitle");
Same goes for any other views you put in your toolbar. Hope it helps.
Add this line at the top:
"xmlns:app="http://schemas.android.com/apk/res-auto"
and then use:
app:showasaction="ifroom"
To control the location of the title you may want to set a custom font as explained here (by twaddington): Link
Then to relocate the position of the text, in updateMeasureState() you would add p.baselineShift += (int) (p.ascent() * R);
Similarly in updateDrawState() add tp.baselineShift += (int) (tp.ascent() * R);
Where R is double between -1 and 1.
Related
I am trying to create an autocomplete control in Xamarin Android based layout. I am using MVVMCross.
I have created the following AXML layout in my fragment.
<TextView
android:text="Item"
android:layout_column="0"
android:id="#+id/textView42"
android:layout_height="28.6dp"
android:layout_width="86.9dp"
android:gravity="center"
android:layout_marginTop="17.5dp"
android:layout_marginRight="17.5dp" />
<AutoCompleteTextView
android:id="#+id/autocomplete_country"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
local:MvxBind="Adapter Items" />
I had updated my viewmodel to return a string array.
private string[] _items = new string[] { "DD", "DD2" };
public String[] Items
{
get { return this._items; }
set { this._items = value; RaisePropertyChanged<string[]>(() => this._items); }
}
I think I need to use an ArrayAdapter, However I am not sure how to do it. Please provide some guidance/pointers which can help me to proceed.
I am new to Xamarin and MVVMCross, so I might be missing things here.
You don't need a custom Adapter. Switch to MvxAutoCompleteTextView and use MVVMCross binding. Here is an example of how I've used it.
<MvxAutoCompleteTextView
android:id="#+id/DrugName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:completionThreshold="1"
android:hint="Enter drug name..."
android:textSize="#dimen/text_size_xlarge"
local:MvxItemTemplate="#layout/item_drug_notclickable"
local:MvxBind="ItemsSource DrugSuggestions; PartialText DrugSearchTerm; SelectedObject Drug;"
android:layout_gravity="right" />
As Nikola said in the comments you want to be calling RaisePropertyChanged against the Items property not the private variable. That goes for all property changed calls.
One thing to watch out for with the AutoComplete is that changes to the Text must result in a change to the ItemSource. Have a look at this GitHub answer for a full explanation, https://github.com/MvvmCross/MvvmCross/issues/945 of the gotcha
In a custom TemplatedControl I have an ItemsControl that is populated outside of the custom TemplatedControl. I want the (future) children of the ItemsControl to automatically inherit the Foreground value from the ItemsControl.
I want to be able to change the Foreground value from the TemplatedControl, and have the child controls update their Foreground as well.
Here's the ItemsControl I have:
<ItemsControl x:Name="PrimaryItems" ItemsSource="{TemplateBinding PrimaryItems}" Foreground="{TemplateBinding MyCustomForeground}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
And when I use the TemplatedControl, it'll look like this:
<Grid>
<Controls:MyCustomControl MyCustomForeground="Blue">
<Controls:MyCustomControl.PrimaryItems>
<Button Content="Test button"/>
</Controls:MyCustomControl.PrimaryItems>
</Controls:MyCustomControl>
</Grid>
I want the Button foreground to automatically be Blue, since that's what I set as MyCustomForeground in my TemplatedControl.
Any tips?
Have you tried {TemplateBinding xxxx} ?
<Controls:MyCustomControl MyCustomForeground="{TemplateBinding Foreground}">
<Controls:MyCustomControl.PrimaryItems>
<Button Content="Test button"/>
</Controls:MyCustomControl.PrimaryItems>
</Controls:MyCustomControl>
I see, this one is tricky. If you try to use DependencyProperty heritage, this won't work (using the foreground property of your UserControl) :
<UserControl
x:Class="TestApp1.CustomControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TestApp1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400" Foreground="Blue">
<StackPanel x:Name="PART_Container">
<Button Content="Test"/>
<TextBlock Text="Test"/>
</StackPanel>
</UserControl>
If you try this snippet, the phone's template for the button will override Foreground="Blue" and thus it will have a white (or black depending on the theme) foreground. Note that Textblock is not styled, and won't display this behavior, it will successfully inherit the blue foreground from its parent UserControl.
How to walkaround this ? You seem to declare a custom dependency property MyCustomForeground, so you can implement a logic in the DependencyPropertyChanged handler. But you also have to apply your custom foreground each time your PrimaryItems changes.
Here's a working sample :
public sealed partial class CustomControl : UserControl
{
public CustomControl()
{
this.InitializeComponent();
}
public Brush MyCustomForeground
{
get { return (Brush)GetValue(MyCustomForegroundProperty); }
set { SetValue(MyCustomForegroundProperty, value); }
}
// Using a DependencyProperty as the backing store for MyCustomForeground. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyCustomForegroundProperty =
DependencyProperty.Register("MyCustomForeground", typeof(Brush), typeof(CustomControl), new PropertyMetadata(null, OnCustomForegroundChanged));
private static void OnCustomForegroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CustomControl ctrl = (CustomControl)d;
ctrl.ApplyCustomForeground();
}
public UIElement PrimaryItems
{
get { return (UIElement)GetValue(PrimaryItemsProperty); }
set { SetValue(PrimaryItemsProperty, value); }
}
// Using a DependencyProperty as the backing store for PrimaryItems. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PrimaryItemsProperty =
DependencyProperty.Register("PrimaryItems", typeof(UIElement), typeof(CustomControl), new PropertyMetadata(null, OnPrimaryItemsChanged));
private static void OnPrimaryItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CustomControl ctrl = (CustomControl)d;
// PART_Container is where I store my PrimaryItems
ctrl.PART_Container.Children.Clear();
ctrl.PART_Container.Children.Add((UIElement)e.NewValue);
ctrl.ApplyCustomForeground();
}
private void ApplyCustomForeground()
{
// PART_Container is where I store my PrimaryItems
foreach (var child in PART_Container.Children)
{
// Foreground is inherited by Control or TextBlock, or other classes...
// You would be better off using reflection here but that's off topic
if (child is Control)
{
((Control)child).Foreground = MyCustomForeground;
}
else if (child is TextBlock)
{
((TextBlock)child).Foreground = MyCustomForeground;
}
}
}
}
Is it possible for a ButtonBar to only show some of the navigator contents in the viewstack. Like, say I only want the button bar to display the button for the first two, and not the third?
<s:ButtonBar dataProvider="{mainViewStack}"/>
<mx:ViewStack id="mainViewStack">
<s:NavigatorContent>
</s:NavigatorContent>
<s:NavigatorContent>
</s:NavigatorContent>
<s:NavigatorContent>
</s:NavigatorContent>
</mx:ViewStack>
I had similar problem and didn't found native solution. So I wrote next "hack".
Wrote own class that extends NavitagorContent.
ExtNavigatorContent.as
public class ExtNavigationContent extends NavigatorContent
{
public function ExtNavigationContent()
{
super();
}
private var _includeInBar:Boolean = true;
public function get includeInBar():Boolean
{
return _includeInBar;
}
public function set includeInBar(value:Boolean):void
{
_includeInBar = value;
dispatchEvent(new Event("labelChanged"));
}
}
I created new skin for TabBar (in your case it will be ButtonBar). Then, add next in the standard skin (instead of default ButtonBarButton creating):
<s:ButtonBarButton includeInLayout="{data.includeInBar}" visible="{data.includeInBar}" buttonMode="true"/>
And, at last, declared TabBar/ButtonBar:
<s:TabBar id="menu" dataProvider="{viewstack1}" skinClass="design.skins.tabbar.SimpleTabBarSkin"/>
<mx:ViewStack id="viewstack1" width="100%" height="100%">
<components:ExtNavigationContent includeInBar="false" />
<components:ExtNavigationContent includeInBar="true" />
<components:ExtNavigationContent />
</mx:ViewStack>
I'm using a data grid with an item renderer that creates a toggle button. The idea is to have a list of items and only allow one to be selected and pre-select one at start.
I've got the single button selection working, meaning that when I click on on toggle button, the others are deselected.
My problem is to create a way of pre-selecting an element of the data grid or simulate a click on a row and selecting the corresponding toggle button.
If I use the datagrid.selectedIndex the result is a selection but the toggle button doesn't get selected.
Here is the code example
In this example I am using the array value "selected" to define selected button, not my favourite solution but the one that worked first.
The array collection:
public static const ValuesList : ArrayCollection = new ArrayCollection(
[
{ID:0, Name:"One", selected:false},
{ID:1, Name:"Two", selected:false},
{ID:2, Name:"Three", selected:false},
{ID:3, Name:"Four", selected:false}
]
);
The data grid:
<s:DataGrid id="dataGrid" dataProvider={ValuesList} >
<s:columns>
<s:ArrayList>
<s:GridColumn id="GridCol0" />
<s:GridColumn id="GridCol1" />
<s:GridColumn id="GridCol2" itemRenderer = "detail_ItemRenderer" />
</s:ArrayList>
</s:columns>
</s:DataGrid>
The column item renderer:
<?xml version="1.0" encoding="utf-8"?>
<s:GridItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
clipAndEnableScrolling="true">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
/**
* Creatioon complete event handler to set toggle button content value.
* */
protected function MyToggleButton_creationCompleteHandler(event:FlexEvent) : void
{
MyListToggleButton.label = data.Name;
MyToggleButton.selected = data.selected;
}
/**
* One of the only function that are called on item interaction.
* */
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth,unscaledHeight);
MyToggleButton.selected = data.selected;
}
]]>
</fx:Script>
<s:ToggleButton id="MyToggleButton" width="100%" height="100%"
creationComplete="MyToggleButton_creationCompleteHandler(event)" />
</s:GridItemRenderer>
SOLUTION:
Using the data array to pass information into the Toggle button. Setting one value to "true" will display the selected element.
To insure data integrity I advise to set the selected index of the grid to the corresponding index of the value set to "true":
public function SetSelectedIndexByName() : int
{
for (var i : int=0; i < dataGrid.dataProvider.length ; i++)
{
if (dataGrid.dataProvider[i].toString().toUpperCase() == "TRUE")
{
return i;
}
}
return -1;
}
In your GridItemRenderer, you should overwrite the setter of data. Your data will contain a variable defining the selection of the ToggleButton. In the setter you will be able to toggle the button based on this variable.
To have only one button selected, you may use a static variable in your renderer which stores the selected itemrenderer. This one is simpler but not really clean since it's a matter of data, not renderers. So you may listen for any change in the dataProvider from a higher level and ensure there's only one item selected.
In a Flex 4 app (with Spark components) I have a ViewStack with various screens, and a TabBar to navigate between them. I'd like the screens to be able to "blink" their tab when something happens in them (like Windows task bar buttons).
How can I do this? My idea is to hack the blinking state into the screen's label (inherited from NavigatorContent) by putting a * in it when blinking, and somehow reading that in a custom tab bar skin.
Is there an easier way? If now, how exactly can I implement mine?
This is a bit hard to explain since it isn't the easiest thing to do, but I'll give it my best. I would create a <s:TabBar /> with a dataProvider of an array of all views in your viewstack and create a custom item renderer for your TabBar which then contains a custom component that extends ButtonBarButton that has a blinking property that's 2-way binded and a custom skin to actually show it blinking, like this:
(man that was a mouthful)
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:local="*">
<fx:Script>
<![CDATA[
import mx.collections.ArrayList;
]]>
</fx:Script>
<s:TabBar dataProvider="{new ArrayList([view1,view2])}">
<s:itemRenderer>
<fx:Component>
<local:BlinkingTab label="{data.label}" blink="#{data.isBlinking}" skinClass="BlinkingTabSkin" />
</fx:Component>
</s:itemRenderer>
</s:TabBar>
<mx:ViewStack>
<local:Foo id="view1" label="View 1" />
<local:Foo id="view2" label="View 2" />
</mx:ViewStack>
</s:Application>
In this case, my views extends 'NavigatorContent', however, we need to be able to express a boolean flag to say that the tab needs to blink, like so:
<s:NavigatorContent xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
implements="ITabView">
<fx:Script>
<![CDATA[
private var _blink:Boolean = false;
[Bindable]
public function get isBlinking():Boolean
{
return this._blink;
}
public function set isBlinking(value:Boolean):void
{
this._blink = value;
}
]]>
</fx:Script>
</s:NavigatorContent>
You'll notice that the view is implementing ITabView. That's only there for typesafing the 'isBlinking' property, but it's optional. When you want your tab to blink, you just need to set this to 'true'. But now we need to get the tab to actually blink. In the custom component 'BlinkingTab' we created for the TabBar, we need to take in the blink property and change the skin state appropriately like so:
<s:ButtonBarButton xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
private var _blink:Boolean;
[Bindable]
public function get blink():Boolean
{
return this._blink;
}
public function set blink(value:Boolean):void
{
this._blink = value;
}
override protected function getCurrentSkinState():String
{
if(!selected && enabled && this._blink)
{
return super.getCurrentSkinState()+'Blinking';
}else{
return super.getCurrentSkinState();
}
}
override protected function mouseEventHandler(event:Event):void
{
super.mouseEventHandler(event);
if(event.type == MouseEvent.CLICK)
{
blink = false;
}
}
]]>
</fx:Script>
</s:ButtonBarButton>
You'll notice that the skin state will only have the 'blinking' string on it if it's enabled and not selected. If it is selected, it won't blink; and if the user clicks on the tab, it will remove the blinking flag which should propagate back to the view (I'm not certain about this part, could always override the 'selected' property or something). And the last part is the skin; you need to create a custom skin so that you can add a blinking animation to your tab. Just create a new skin with a ButtonBarButton host component that uses the TabBarButtonSkin and add these new states:
<s:State name="upBlinking" basedOn="up" stateGroups="blinking" />
<s:State name="overBlinking" basedOn="over" stateGroups="blinking" />
<s:State name="downBlinking" basedOn="down" stateGroups="blinking" />
From here, you need to create your own state based blinking. This is not fully tested, but I think I helped you get 95% of the way. Hope this helps.
BTW, this method is 100% legit. No hacking and you can reuse every single part of the code for somewhere else :)