I was looking at Netflix app and their scrolling behaviour.I would like to do the same but don't know where to start. I know how to override LayoutManager for RecyclerView(though I don't to save that as last resort). Is there easier way to control scrolling speed using RowPresenter ?
Implement the logic on your activity class, ovverride keys (OnKeyDown) on a way that will ignore the key event for 3-4 millis or sth.
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
long current = System.currentTimeMillis();
boolean res = false;
if (current - mLastKeyDownTime < 300) {
res = true;
} else {
res = super.onKeyDown(keyCode, event);
mLastKeyDownTime = current;
}
return res;
}
Leanback supports setting scrolling speed for BaseGridView from version 1.1.0-alpha03. You can set scrolling speed by using a method named setSmoothScrollSpeedFactor in all sub-classes of BaseGridView.
Related
I am trying to port an Xamarin.Forms app to .NET MAUI but have run into the deprecation of Device.StartTimer, whilst this obviously currently still works in MAUI, I am interested to find out what the alternative is?
Currently I have a wrapper class as follows:
public void Start()
{
if (IsRunning)
{
return;
}
var wrapper = new TaskWrapper(Task, IsRecurring, true);
Tasks.Add(wrapper);
Device.StartTimer(Interval, wrapper.RunTask);
}
I tried replacing this with a System.Timers.Timer however this led to the issue of not being able to modify UI elements due to being on the wrong thread? The timer wrapper itself is used in multiple places so I can't use binding for example in this case either.
Is there actually a direct replacement for Device.StartTimer? Any assistance is greatly appreciated.
The Device timer is obsolete in MAUI.
You can create an IDispatcherTimer instance and subscribe to the Tick event like this:
var timer = Application.Current.Dispatcher.CreateTimer();
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += (s,e) => DoSomething();
timer.Start();
Depending on the context that you're using the timer in, you should use the MainThread.BeginInvokeOnMainThread() method to update UI elements:
void DoSomething()
{
MainThread.BeginInvokeOnMainThread(() =>
{
//Update view here
});
}
More info on UI main thread:
https://learn.microsoft.com/en-us/dotnet/maui/platform-integration/appmodel/main-thread
IDispatcherTimer timer;
timer = Dispatcher.CreateTimer();
timer.Interval = TimeSpan.FromMilliseconds(1000);
timer.Tick += (s, e) =>
{
label.Text = DateTime.Now.ToString();
};
timer.Start();
https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/button#press-and-release-the-button
How do I set the value of SpinnerValueFactory<LocalTime> valueFactory = new SpinnerValueFactory<LocalTime>() in a spinner?
Attempting to set and display the value in the spinner editor with hrsSpinner.getValueFactory().setValue(valueFactory); , Java tells me:
The method setValue(LocalTime) in the type SpinnerValueFactory<LocalTime> is not applicable for the arguments (SpinnerValueFactory<LocalTime>).
Another approach I've tried is hrsSpinner.setValueFactory(valueFactory);. I get no errors. But the hours are initially not displayed in the spinner editor until I've clicked on the spinner to increment or decrement the hours.
Any ideas how I can solve my problem? Here is a sample of my code I'm using
Calendar calendar = new GregorianCalendar();
int hour;
this.hour = calendar.get(Calendar.HOUR_OF_DAY);
if(hourRadioButton.isSelected() == true)
{
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh");
ObservableList<String> hours = FXCollections.observableArrayList(String.valueOf(hour));
SpinnerValueFactory<LocalTime> valueFactory = new SpinnerValueFactory<LocalTime>() {
{
setConverter(new LocalTimeStringConverter(formatter,null));
}
#Override
public void decrement(int steps) {
if(getValue() == null)
{
setValue(LocalTime.now());
}
else
{
LocalTime localTime = (LocalTime) getValue();
setValue(localTime.minusHours(1));
}
}
#Override
public void increment(int steps) {
if (this.getValue() == null)
{
setValue(LocalTime.now());
}
else
{
LocalTime localTime = (LocalTime) getValue();
setValue(localTime.plusHours(1));
}
}
};
//hrsSpinner.setValueFactory(valueFactory);
hrsSpinner.getValueFactory().setValue(valueFactory);
}
Thank you very much in advance for your time and help!!!
AvJoe
hrsSpinner.getValueFactory() is an expression of type SpinnerValueFactory<LocalTime>. Using setValue on this is not possible with this parameter and it wouldn't do what you want.
Use the setter for the valueFactory property instead:
hrsSpinner.setValueFactory(valueFactory);
This way of setting the value factory seems to stem from the bad practice of using the setter of the property object instead of using the more concise and thus easier to parse version using the setter corresponding to the property. The version using the property object's setter would be
hrsSpinner.valueFactoryProperty().set(valueFactory);
in this case.
Furthermore I don't recommend mixing Calendar and java.time API. If you've got the choice, go with the java.time api completely. It provides the same functionality and is much easier to use imho. E.g. assuming you wanted to use the calendar to truncate the value to full hours, you could use truncateTo instead:
SpinnerValueFactory<LocalTime> factory = new SpinnerValueFactory<LocalTime>() {
{
setValue(defaultValue());
}
private LocalTime defaultValue() {
return LocalTime.now().truncatedTo(ChronoUnit.HOURS);
}
#Override
public void decrement(int steps) {
LocalTime value = getValue();
setValue(value == null ? defaultValue() : value.minusHours(steps));
}
#Override
public void increment(int steps) {
LocalTime value = getValue();
setValue(value == null ? defaultValue() : value.plusHours(steps));
}
};
Spinner<LocalTime> spinner = new Spinner<>();
spinner.setValueFactory(factory);
In my app, I have a searchbox which allows users to filter as they type. For some reason I can't get an InfinteProgress to properly display while the filtering is being executed.
Here's my code:
Pass 1
public void renderForumList(){
try{
magnify = mStateMachine.findForumSearchIcon(form);
}catch(NullPointerException ex){
System.out.println("User typed additional character in search term before previous term finished executing");
}
InfiniteProgress infi = new InfiniteProgress();
magnify.getParent().replace(magnify, infi, null);
Display.getInstance().invokeAndBlock(new Runnable() {
#Override
public void run() {
for (int i = 0;i < containerStates.length;i++){
if(containerStates[i] != listItems[i].isVisible()){
listItems[i].setHidden(!containerStates[i]);
listItems[i].setVisible(containerStates[i]);
}
}
Display.getInstance().callSerially(new Runnable() {
#Override
public void run() {
mStateMachine.findForumsListComponent(form).animateLayout(200);
mStateMachine.findContainer2(form).replace(infi, magnify, null);
}
});
}
});
}
In this version, the infinite progress shows up in the proper position, but it doesn't spin.
Pass 2
public void renderForumList(){
try{
magnify = mStateMachine.findForumSearchIcon(form);
}catch(NullPointerException ex){
System.out.println("User typed additional character in search term before previous term finished executing");
}
InfiniteProgress infi = new InfiniteProgress();
magnify.getParent().replace(magnify, infi, null);
for (int i = 0;i < containerStates.length;i++){
if(containerStates[i] != listItems[i].isVisible()){
listItems[i].setHidden(!containerStates[i]);
listItems[i].setVisible(containerStates[i]);
}
}
mStateMachine.findForumsListComponent(form).animateLayout(200);
mStateMachine.findContainer2(form).replace(infi, magnify, null);
}
}
}
In this version, the magnifier icon just flashes briefly, but the InfiniteProgress spinner is never visible.
I get the same results on the simulator and on an Android device.
How can I get the InfiniteProgress to spin while the search is taking place?
invokeAndBlock opens a new thread and thus violates the EDT as you access UI components on a separate thread.
Try using callSerially instead to postpone the following code into the next EDT cycle although I'm not sure that will help as everything is still happening on the EDT.
Alternatively I'm guessing the method isVisible takes time, so you can enclose that call alone in invokeAndBlock.
To understand invokeAndBlock check out the developer guide https://www.codenameone.com/manual/edt.html
I have a VideoDisplay instance playing some video. When I click on the video slider (also my component) the property videoDisplay.playheadTime is set and the videoDisplay.state goes from 'playing' into a 'seeking' state for a brief moment (the videoDisplay seeks for a new position and then plays the video again). Intended bevaiour.
But if I'm (or any random user) fast enough, I can set the playheadTime again while the player is still in 'seeking' state. When repeated several times every click is enqueued and the videoDisplay jump on every place of the video I have clicked(this is happening in an interval about 10-15 second after my last click). When I use live dragging the videoDisplay, overwhelmed by seekings, goes into 'error' state.
My question is - is there any way to cancel seeking state of the VideoDisplay class? For example player is in 'seeking' state, I set playheadTime, and the player forgets about last seeking and try to find the new place of the video.
I will downvote pointless answers like 'Use the Flex4 VideoPlayer class'!
One possible way is wrap the video display in a component and manage the seek a little better yourself. So if someone calls seek, make sure that the video is not currently seeking, if so, then wait till the current operation is complete before proceeding to the new one. If the user tries to seek again, discard all currently pending operations and make the latest one the next operation. Working on this exact problem right now.... Here's the code:
public function Seek(nSeconds:Number, bPlayAfter:Boolean):void
{
trace("Player Seek: "+ nSeconds);
var objSeekComand:VideoPlayerSeekCommand = new VideoPlayerSeekCommand(ucPlayer, nSeconds, bPlayAfter);
ProcessCommand(objSeekComand);
}
protected function ProcessCommand(objCommand:ICommand):void
{
if(_objCurrentCommand != null)
{
_objCurrentCommand.Abort();
}
_objCurrentCommand = objCommand
objCommand.SignalCommandComplete.add(OnCommandComplete);
objCommand.Execute();
}
Here's the Command
public class VideoPlayerSeekCommand extends CommandBase
{
private var _ucVideoDisplay:VideoDisplay;
private var _nSeekPoint:Number;
private var _bPlayAfterSeek:Boolean;
private var _bIsExecuting:Boolean;
public function VideoPlayerSeekCommand(ucVideoDisplay:VideoDisplay, nSeekPointInSeconds:Number, bPlayAfterSeek:Boolean, fAutoAttachSignalHandler:Function = null)
{
_ucVideoDisplay = ucVideoDisplay;
_nSeekPoint = nSeekPointInSeconds;
_bPlayAfterSeek = bPlayAfterSeek;
super(fAutoAttachSignalHandler);
}
override public function Execute():void
{
//First check if we are playing, and puase if needed
_bIsExecuting = true;
if(_ucVideoDisplay.playing == true)
{
_ucVideoDisplay.addEventListener(MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE, OnPlayerStateChangedFromPlay, false, 0, true);
_ucVideoDisplay.pause();
}
else
{
DoSeek();
}
}
protected function OnPlayerStateChangedFromPlay(event:MediaPlayerStateChangeEvent):void
{
_ucVideoDisplay.removeEventListener(MediaPlayerStateChangeEvent.MEDIA_PLAYER_STATE_CHANGE, OnPlayerStateChangedFromPlay);
if(_bIsExecuting == true)
{
if(_ucVideoDisplay.playing == false)
{
DoSeek();
}
else
{
throw new Error("VideoPlayerSeekAndPlayCommand - OnPlayerStateChangedFromPlay error");
}
}
}
private function DoSeek():void
{
if(_bIsExecuting == true)
{
_ucVideoDisplay.seek(_nSeekPoint);
CheckSeekComplete();
}
}
private function CheckSeekComplete():void
{
if(_bIsExecuting == true)
{
if (Math.abs( _ucVideoDisplay.currentTime - _nSeekPoint) < 2)
{
if(_bPlayAfterSeek == true)
{
_ucVideoDisplay.play();
}
DispatchAndDestroy();
}
else
{
CoreUtils.CallLater(CheckSeekComplete, .07);
}
}
}
override public function Abort():void
{
_bIsExecuting = false;
SignalCommandComplete.removeAll();
}
}
Im Using AS3 Signals here instead of events, and the CoreUtils.Call later you can use setInterval, or a Timer. But the idea is to not call seek until the video is paused, and to keep track of when the seek is complete.
I am trying to write an application, but it is constantly crashing when using the uiimagepickercontroller. I thought that it might be because I was not disposing of the picker after each use, but it will often freeze up on first run as well. Usually I'll take a picture and it just freezes, never asking to "use" the picture.
Do you have any suggestions? Here is my code. Has anyone gotten this to work?
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
myPicker = new UIImagePickerController();
myPicker.Delegate = new myPickerDelegate(this);
myAlbumButton.Clicked += delegate {
if(UIImagePickerController.IsSourceTypeAvailable(UIImagePickerControllerSourceType.PhotoLibrary)){
myPicker.SourceType = UIImagePickerControllerSourceType.PhotoLibrary;
myPicker.AllowsEditing = true;
this.PresentModalViewController (myPicker, true);
}else{
Console.WriteLine("cannot get album");
}
};
myCameraButton.Clicked += delegate {
if(UIImagePickerController.IsSourceTypeAvailable(UIImagePickerControllerSourceType.Camera)){
myPicker.SourceType = UIImagePickerControllerSourceType.Camera;
//myPicker.AllowsEditing = true;
this.PresentModalViewController (myPicker, true);
}else{
Console.WriteLine("cannot get camera");
}
};
}
private class myPickerDelegate : UIImagePickerControllerDelegate
{
private TestView _vc;
public myPickerDelegate ( TestView controller):base()
{
_vc = controller;
}
public override void FinishedPickingImage (UIImagePickerController myPicker, UIImage image, NSDictionary editingInfo)
{
// TODO: Implement - see: http://go-mono.com/docs/index.aspx?link=T%3aMonoTouch.Foundation.ModelAttribute
_vc.myImageView.Image = image;
myPicker.DismissModalViewControllerAnimated(true);
}
}
Try to call your event handlers code from the main thread by using BeginInvokeOnMainThread().
So my issue was very similar.
Instead of having a delegate class, I had the delegates inline for the picker.
For some reason the app froze every time after talking the image, not stopping in any breakpoint after that.
The solution that worked for me was to use this book:
http://www.scribd.com/doc/33770921/Professional-iPhone-Programming-with-MonoTouch-and-NET-C