ImageButtonGestureRecognizers for custom button - xaml
I have a collection view with my custom buttons.And have a function which change color of images and their background.My problem: it works with every button if I click,I want to make function which will change color and backgroundcolor of my SELECTED button.I thought maybe to make controls:CustomRoundButton.GestureRecognizers but I don`t understand how to write this function.Or maybe other function
<CollectionView ItemsSource="{Binding Cars}"
SelectionMode="None">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical"
Span="3" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="90" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<Frame CornerRadius="10" BorderColor="Black" Margin="5,5,5,5" Padding="0"
>
<controls:CustomRoundButton TintColor="#616161" HeightRequest="90"
WidthRequest="90" CornerRadius="10" HorizontalOptions="Center"
BackgroundColor="White" ImageSource="heart" Clicked="Button_OnClicked"/>
</Frame>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
private void Button_OnClicked(object sender, EventArgs e)
{
((CustomRoundButton)sender).BackgroundColor = ((Button)sender).BackgroundColor == Color.White
? Color.FromHex("#2979FF")
: Color.White;
CustomRoundButton.SetTintColor((CustomRoundButton)sender, CustomRoundButton.GetTintColor((CustomRoundButton)sender) == Color.FromHex("#616161")
? Color.White
: Color.FromHex("#616161"));
}
enter image description here
I notice you used MVVM, you can achieve it my Command for your controls:CustomRoundButton .
Here is layout code.
<StackLayout>
<!-- Place new controls here -->
<CollectionView x:Name="BillView" ItemsSource="{Binding Cars}"
SelectionMode="None">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical"
Span="3" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="90" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<Frame CornerRadius="10" BorderColor="Black" Margin="5,5,5,5" Padding="0" >
<controls:CustomRoundButton HeightRequest="90"
WidthRequest="90" CornerRadius="10" HorizontalOptions="Center"
BackgroundColor="{Binding CustButtonColor}" ImageSource="{Binding Image}" Command="{ Binding BindingContext.ChangeCommand, Source={x:Reference Name=BillView} }" CommandParameter="{Binding .}"/>
</Frame>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
Here is layout background code.
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
this.BindingContext = new MyModelView();
}
}
Here is code about MyModelView.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Text;
using System.Windows.Input;
using Xamarin.Forms;
namespace CollectionViewDemo
{
public class MyModelView
{
public ObservableCollection<Car> Cars { get; set; }
public ICommand ChangeCommand { protected set; get; }
public MyModelView()
{
Cars = new ObservableCollection<Car>();
Cars.Add(new Car() { Image = "unheart.png",CustButtonColor=Color.Default });
Cars.Add(new Car() { Image = "unheart.png", CustButtonColor = Color.Default });
Cars.Add(new Car() { Image = "unheart.png", CustButtonColor = Color.Default });
Cars.Add(new Car() { Image = "unheart.png", CustButtonColor = Color.Default });
Cars.Add(new Car() { Image = "unheart.png", CustButtonColor = Color.Default });
Cars.Add(new Car() { Image = "unheart.png", CustButtonColor = Color.Default });
Cars.Add(new Car() { Image = "unheart.png", CustButtonColor = Color.Default });
Cars.Add(new Car() { Image = "unheart.png", CustButtonColor = Color.Default });
Cars.Add(new Car() { Image = "unheart.png", CustButtonColor = Color.Default });
ChangeCommand = new Command<Car>((key) =>
{
var car = key as Car;
if (car.Image == "heart.png")
{
car.Image = "unheart.png"; car.CustButtonColor = Color.Default;
}
else
{
car.Image = "heart.png";
car.CustButtonColor = Color.Red;
}
});
}
}
public class Car: INotifyPropertyChanged
{
string image;
public string Image
{
set
{
if (image != value)
{
image = value;
OnPropertyChanged("Image");
}
}
get
{
return image;
}
}
Color _custButtonColor;
public Color CustButtonColor
{
set
{
if (_custButtonColor != value)
{
_custButtonColor = value;
OnPropertyChanged("CustButtonColor");
}
}
get
{
return _custButtonColor;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Here is running GIF.
======Update=======
but can you help me that only ONE selected button change background color and color of the icon on the image?
Do you want to achieve the result following GIF?
If so, please change the code of ChangeCommand.
ChangeCommand = new Command<Car>((key) =>
{
foreach (var item in Cars)
{
item.Image = "unheart.png";
item.CustButtonColor = Color.Default;
}
var car = key as Car;
car.Image = "heart.png";
car.CustButtonColor = Color.Red;
});
=======Update2=========
If you have different pictures, you can add a property called Name. we can reset ObservableCollection by name, when we click the Button.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Text;
using System.Windows.Input;
using Xamarin.Forms;
namespace CollectionViewDemo
{
public class MyModelView
{
public ObservableCollection<Car> Cars { get; set; }
public ICommand ChangeCommand { protected set; get; }
public MyModelView()
{
Cars = new ObservableCollection<Car>();
Cars.Add(new Car() {Name="Audio", Image = "unheart.png",CustButtonColor=Color.Default });
Cars.Add(new Car() {Name="BMW", Image = "faded_div.jpg", CustButtonColor = Color.Default });
Cars.Add(new Car() {Name="BENZ", Image = "myIcon.png", CustButtonColor = Color.Default });
Cars.Add(new Car() {Name="LEUX", Image = "wind.jpg", CustButtonColor = Color.Default });
var DefaultCars= new ObservableCollection<Car>();
DefaultCars = Cars;
ChangeCommand = new Command<Car>((key) =>
{
//Rest the `ObservableCollection` by name.
foreach (var item in Cars)
{
if (item.Name.Equals("Audio"))
{
item.Image = "unheart.png";
item.CustButtonColor = Color.Default;
}else if (item.Name.Equals("BMW"))
{
item.Image = "faded_div.jpg";
item.CustButtonColor = Color.Default;
}
else if (item.Name.Equals("BENZ"))
{
item.Image = "myIcon.png";
item.CustButtonColor = Color.Default;
}
else if (item.Name.Equals("LEUX"))
{
item.Image = "wind.jpg";
item.CustButtonColor = Color.Default;
}
}
var car = key as Car;
car.Image = "heart.png";
car.CustButtonColor = Color.Red;
});
}
}
public class Car: INotifyPropertyChanged
{
public string Name { get; set; }
string image;
public string Image
{
set
{
if (image != value)
{
image = value;
OnPropertyChanged("Image");
}
}
get
{
return image;
}
}
Color _custButtonColor;
public Color CustButtonColor
{
set
{
if (_custButtonColor != value)
{
_custButtonColor = value;
OnPropertyChanged("CustButtonColor");
}
}
get
{
return _custButtonColor;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Here is running GIF.
=========Update 3==============
You can set different icon for different button when you click it(set the different image by Name).
ChangeCommand = new Command<Car>((key) =>
{
//reset the item to default image and background.
foreach (var item in Cars)
{
if (item.Name.Equals("Audio"))
{
item.Image = "unheart.png";
item.CustButtonColor = Color.Default;
}else if (item.Name.Equals("BMW"))
{
item.Image = "faded_div.jpg";
item.CustButtonColor = Color.Default;
}
else if (item.Name.Equals("BENZ"))
{
item.Image = "myIcon.png";
item.CustButtonColor = Color.Default;
}
else if (item.Name.Equals("LEUX"))
{
item.Image = "wind.jpg";
item.CustButtonColor = Color.Default;
}
}
//set the different image by Name
var car = key as Car;
if (car.Name.Equals("BENZ"))
{
car.Image = "sandwich.png";
}else if (car.Name.Equals("BMW"))
{
car.Image = "rose.png";
}
else
{
car.Image = "heart.png";
}
car.CustButtonColor = Color.Red;
});
}
}
Here is running GIF.
Here is demo.
https://github.com/851265601/Xamarin.Android_ListviewSelect/blob/master/CollectionViewDemo.zip
Related
How to change the resolution of AgoraVideoPlayer?
import React from 'react'; import { AgoraVideoPlayer } from "agora-rtc-react"; import { Grid } from "#material-ui/core"; import { useState, useEffect } from "react"; import { useClient } from './settings'; import "./Video.css"; export default function Video(props) { const { users, tracks } = props; const client = useClient(); const [gridSpacing, setGridSpacing] = useState(12); useEffect(() => { setGridSpacing(Math.max(Math.floor(12 / (users.length + 1)), 4)); }, [users, tracks]); return ( <Grid container className="VidCon"> <Grid item xs={gridSpacing} className="vidd"> <Grid id='Name'>{client.uid}</Grid> <AgoraVideoPlayer className="Video" videoTrack={tracks[1]} /> </Grid> {users.length > 0 && users.map((user) => { if (user.videoTrack) { return ( <Grid item xs={gridSpacing} className="vidd"> <Grid id='Name'>{user.uid}</Grid> <AgoraVideoPlayer className="Video" videoTrack={user.videoTrack} key={user.uid} /> </Grid> ); } else return null; })} </Grid> ); } This is the sample code of me using the AgoraVideoPlayer in my react.js app. It works, but i want to change the resolution and aspect ratio of the video that is captured. Please help.
I can't view the picture in the firebase storage
my get code ` getImage = async (imageUrl) => { const user = firebase.auth().currentUser; const url = await storage() .ref(`Users/${user.uid}/picture`) .getDownloadURL() console.log(url) imageUrl = url; let imgSource = { uri: imageUrl }; // change this line if (isNaN(imageUrl)) { imgSource = { uri: this.state.imageUrl }; if (Platform.OS == 'android') { imgSource.uri = "file:///" + imgSource.uri; } } } render() { let {imageUrl} = this.state; return ( <View> <Image source={this.getImage(imageUrl)} style={{width:200,height:200}}/> </View> ` console screenshot I can get the url from my console but the picture is not showing in my app, what's the problem
This might help state = { newImageURL : undefined } getImage = async (imageUrl) => { const user = firebase.auth().currentUser; const url = await storage() .ref(`Users/${user.uid}/picture`) .getDownloadURL() console.log(url) imageUrl = url; let imgSource = { uri: imageUrl }; // change this line if (isNaN(imageUrl)) { imgSource = { uri: this.state.imageUrl }; if (Platform.OS == 'android') { imgSource.uri = "file:///" + imgSource.uri; } } this.setState({ newImageURL: imgSource }); // update code here } componentDidMount(){ let {imageUrl} = this.state; this.getImage(imageUrl) } render() { return ( <View> {newImageURL ? <Image source={newImageURL} style={{width:200,height:200}}/> : null } </View>
How to extract index from a button in Listview?
I have an unusual question. How to extract index from a button in Listview. I would like <Button Clicked = "indexOne" Text = "{Binding Answers [0]}" /> to have index 0 e.t.c Sorry for my english. <ContentPage.Content> <ScrollView> <ListView x:Name="elo" Margin="10,0" RowHeight="250"> <ListView.ItemTemplate> <DataTemplate> <ViewCell > <Grid > <StackLayout > <Label Text="{Binding Question1}" /> <Button Clicked="indexOne" Text="{Binding Answers[0]}" /> <Button Clicked="indexTwo" Text="{Binding Answers[1]}" /> <Button Clicked="indexThree" Text="{Binding Answers[2]}"/> <Button Clicked="indexFour" Text="{Binding Answers[3]}" /> </StackLayout> </Grid> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> </ScrollView> </ContentPage.Content>
Welcome to SO ! You can use the Xamarin.Forms MessagingCenter to achiece that , not sure this is the best way to achieve that . If later have good solutions will update here . Here is the sample code to explain that .Create a OptionClass to store Option of Question . public class OptionClass { // add QuestionID public int QuestionID { set; get; } public string OptionTitle { set; get; } public int OptionIndex { set; get; } // add int[] for button command paramater public int[] IDValues { get { return new int[] { QuestionID, OptionIndex }; } } public ICommand OptionChooseCommand { set; get; } } Create a QuestionClass class to generate each question data : public class QuestionClass { public string QuestionTitle { set; get; } // add QuestionID public int QuestionID { set; get; } public List<OptionClass> optionClasses { set; get; } public QuestionClass(string title,List<OptionClass> options, int questionId) { optionClasses = new List<OptionClass>(); QuestionTitle = title; optionClasses = options; QuestionID = questionId; } } Last , creat a AnsweViewModel to generate list data of questions : public class AnsweViewModel { private MainPage mainPage; public List<QuestionClass> questionClasses { set; get; } public AnsweViewModel(MainPage mainPage) { this.mainPage = mainPage; questionClasses = new List<QuestionClass>(); List<OptionClass> optionsOne = new List<OptionClass>(); optionsOne.Add(new OptionClass() { QuestionID = 0, OptionTitle = "A", OptionIndex = 1, OptionChooseCommand = new Command<int[]>((int[] value) => { MessagingCenter.Send<MainPage, int[]>(mainPage, "ButtonInfo", value); }) }); optionsOne.Add(new OptionClass() { QuestionID = 0, OptionTitle = "B", OptionIndex = 2, OptionChooseCommand = new Command<int[]>((int[] value) => { MessagingCenter.Send<MainPage, int[]>(mainPage, "ButtonInfo", value); }) }); optionsOne.Add(new OptionClass() { QuestionID = 0, OptionTitle = "C", OptionIndex = 3, OptionChooseCommand = new Command<int[]>((int[] value) => { MessagingCenter.Send<MainPage, int[]>(mainPage, "ButtonInfo", value); }) }); optionsOne.Add(new OptionClass() { QuestionID = 0, OptionTitle = "D", OptionIndex = 4, OptionChooseCommand = new Command<int[]>((int[] value) => { MessagingCenter.Send<MainPage, int[]>(mainPage, "ButtonInfo", value); }) }); questionClasses.Add(new QuestionClass("Question One", optionsOne, 0) { }); List<OptionClass> optionsTwo = new List<OptionClass>(); optionsTwo.Add(new OptionClass() { QuestionID = 1, OptionTitle = "A", OptionIndex = 1, OptionChooseCommand = new Command<int[]>((int[] value) => { MessagingCenter.Send<MainPage, int[]>(mainPage, "ButtonInfo", value); }) }); optionsTwo.Add(new OptionClass() { QuestionID = 1, OptionTitle = "B", OptionIndex = 2, OptionChooseCommand = new Command<int[]>((int[] value) => { MessagingCenter.Send<MainPage, int[]>(mainPage, "ButtonInfo", value); }) }); optionsTwo.Add(new OptionClass() { QuestionID = 1, OptionTitle = "C", OptionIndex = 3, OptionChooseCommand = new Command<int[]>((int[] value) => { MessagingCenter.Send<MainPage, int[]>(mainPage, "ButtonInfo", value); }) }); questionClasses.Add(new QuestionClass("Question Two", optionsTwo, 1) { }); } } Here in Xaml , use Bindable Layouts to show option view . If always options are four items, not need to use this . <ListView x:Name="elo" Margin="10,0" RowHeight="250"> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <Grid> <StackLayout Orientation="Vertical"> <Label Text="{Binding QuestionTitle}" /> <StackLayout BindableLayout.ItemsSource="{Binding optionClasses}" Orientation="Horizontal"> <BindableLayout.ItemTemplate> <DataTemplate> <Button Text="{Binding OptionTitle}" Command="{Binding OptionChooseCommand}" CommandParameter="{Binding IDValues}"/> </DataTemplate> </BindableLayout.ItemTemplate> </StackLayout> </StackLayout> </Grid> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> In ContentPage , code as follow : public partial class MainPage : ContentPage { public MainPage() { InitializeComponent(); AnsweViewModel answeViewModel = new AnsweViewModel(this); elo.ItemsSource = answeViewModel.questionClasses; MessagingCenter.Subscribe<MainPage,int>(this, "ButtonIndex", (sender,arg) => { Console.WriteLine("Question is " + answeViewModel.questionClasses[arg[0]].QuestionTitle + " Button Index is : " + arg[1]); DisplayAlert("Question is " + answeViewModel.questionClasses[arg[0]].QuestionTitle, " Button Index is : " + arg[1], "Ok"); // Do something whenever the "Hi" message is received }); } } The updated effect :(not need to care about which cell be selected )
react dnd nesting level on hover
I want to implement a drop system where if drop targets are nested you can cycle between them using the mouse scroll wheel (or have an automatic cycle happen after a certain amount of time) this will be particularly useful because many of the nested targets occupy the exact same areas on screen. I'm presently thinking that I could use a callback passed down from the container that could be used by drop targets to register/de-register themselves when their hover function is called/when isOver prop changes but it's very coupled and I have to pass props into the DropTargets from the container, in the real world application there will be an unknown number of levels between container and drop target so I'd likely have to set up some kind of callback system, overall its not an ideal solution. Also I'm not sure how to ensure the correct order when cycling as the drop targets don't know how deeply nested they are (see code below for how I've implemented this). Is there a cleaner way to implement such a system? #DragDropContext(HTML5Backend) export default class Container extends Component { constructor (props) { super(props); this.state = { bins: [] }; this.status = {}; this._cycle = this.cycle.bind(this); this._register = this.register.bind(this); this._deregister = this.deregister.bind(this); } componentWillUnmount () { if (this.timer) { window.clearInterval(this.timer); } } register (name) { if (this.state.bins.findIndex(e => e === name) === -1) { this.setState({ bins: this.state.bins.concat(name) }); if (!this.timer) { this.cycledBins = []; this.timer = window.setInterval(this._cycle, 3000); this._cycle(); } } } deregister (name) { if (this.state.bins.findIndex(e => e === name) !== -1) { const bins = this.state.bins.filter((e) => e === name); this.setState({ bins }); if (!bins.length) { window.clearInterval(this.timer); this.timer = undefined; } } } cycle () { this.status = {}; const bins = this.state.bins; let activeBin = -1; for (let i = 0; i < bins.length; i += 1) { if (this.cycledBins.findIndex(bin => bin === bins[i]) === -1) { activeBin = bins[i]; break; } } if (activeBin === -1) { activeBin = bins[0]; this.cycledBins = []; } this.cycledBins.push(activeBin); this.activeBin = activeBin; this.status[activeBin] = { isActive: true, isOnlyActive: bins.length === 1 } this.forceUpdate(); } render () { return ( <div> bins = {JSON.stringify(this.state.bins)}<br /> cycledBins = {JSON.stringify(this.cycledBins)} <div style={{ overflow: 'hidden', clear: 'both' }}> <Dustbin name="Outer" register={this._register} deregister={this._deregister} status={this.status["Outer"]} > <Dustbin name="Middle" register={this._register} deregister={this._deregister} status={this.status["Middle"]} > <Dustbin name="Inner" register={this._register} deregister={this._deregister} status={this.status["Inner"]} /> </Dustbin> </Dustbin> </div> <div style={{ overflow: 'hidden', clear: 'both' }}> <Box name='Glass' /> <Box name='Banana' /> <Box name='Paper' /> </div> </div> ); } } const boxTarget = { hover(props) { props.register(props.name); } }; #DNDDropTarget('Box', boxTarget, (connect, monitor) => ({ connectDropTarget: connect.dropTarget(), isOver: monitor.isOver()//, //canDrop: monitor.canDrop() })) export default class Dustbin extends Component { static propTypes = { connectDropTarget: PropTypes.func.isRequired, isOver: PropTypes.bool.isRequired//, //canDrop: PropTypes.bool.isRequired }; componentWIllMount () { if (!this.props.isOver) { this.props.deregister(this.props.name); } } componentWillReceiveProps (nextProps) { if (nextProps.isOver !== this.props.isOver && !nextProps.isOver) { this.props.deregister(this.props.name); } } render() { const { canDrop, isOver, connectDropTarget, status } = this.props; const isOnlyActive = status && status.isOnlyActive; let isActive = status && status.isActive; if (!isOver && isActive) { isActive = false; } return connectDropTarget( <div> <DropTarget position={BEFORE} shown={isActive} /> { isActive && !isOnlyActive ? '(' + this.props.name + ')' : undefined } { this.props.children ? this.props.children : ( <div style={style}> { isActive ? 'Release to drop' : 'Drag a box here' } </div> ) } <DropTarget position={AFTER} shown={isActive} /> </div> ); } }
How to export View to image data in React Native
I am trying to export the rendered graphic of a View in React Native. For example, my view is like: <View> <Image/> // Some image <Rectangle /> // Some little <Circle /> // Some circle </View> How do I extract the rendered graphic from the container View into whatever image data format?
see https://stackoverflow.com/a/31936516/528842 Subclass RCTView and add export method: MyCoolView.m: - (NSData *)export { UIGraphicsBeginImageContext(self.bounds.size); [self.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return UIImagePNGRepresentation(image); } Expose the export method in the native view manager: The key is to pass in the reactTag which is the reference to the native component. MyCoolViewManager.m: RCT_EXPORT_METHOD(export:(NSNumber *)reactTag callback:(RCTResponseSenderBlock)callback) { [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { MyCoolView *view = viewRegistry[reactTag]; if (![view isKindOfClass:[MyCoolView class]]) { RCTLogMustFix(#"Invalid view returned from registry, expecting MyCoolView, got: %#", view); } NSData * imageData = [view export]; callback(#[[NSNull null], [imageData base64EncodedStringWithOptions:0]]); }]; } Expose export method from React component: MyCoolView.js: var React = require('react-native'); var NativeModules = require('NativeModules'); var MyCoolViewManager = NativeModules.MyCoolViewManager; var findNodeHandle = require('findNodeHandle'); class MyCoolView extends React.Component{ // ... export() { return new Promise((resolve, reject) => { MyCoolViewManager.export( findNodeHandle(this), (error, result) => { if (error) { reject(error); } else { resolve(result); // now we've got the base64 encoded data of the exported image } } ); }); } } Invoke the export method: The component look like this: <MyCoolView ref='myCoolView'> <Image /> <Rectangle /> <Circle /> </View> </MyCoolView> In some function: this.refs.myCoolView.export() .then(base64data => { console.log(base64data); }, error => { console.error(error); });