Bus Seating Arrangement same as in RedBus, Android - android

I am trying to implement a bus seat booking application which is very similar to RedBus app. I came across over the seating arrangement in that and i was struck over there.
I really need your help in achieving this. I have tried with Recycler View but that doesn't get me into the exact layout.
I have tried with recycler view and my layout goes as,
But, the actual screen shot from redbud application is as,
I have gone through one of the code that is available in git
https://github.com/TakeoffAndroid/SeatBookingRecyclerView
But, using the above code, i can get the layout design but the problem with this is, Say like if there are 41 seats, but using the above git code it will be showing 33 seats on the screen comes up like as,
Help would be really appreciated.
Snippet Code:
numOfColumns = 4;
mArrayBusSeats = new ArrayList<>();
for (int i = 0; i < mArraySeats.size(); i++) {
if (i % numOfColumns == 0 || i % numOfColumns == (numOfColumns - 1)) {
BusLayoutModel model = mArraySeats.get(i);
model.setSeatType(AbstractItem.TYPE_EDGE);
mArrayBusSeats.add(model);
} else if (i % numOfColumns == 1 || i % numOfColumns == (numOfColumns - 2)) {
BusLayoutModel model = mArraySeats.get(i);
model.setSeatType(AbstractItem.TYPE_CENTER);
mArrayBusSeats.add(model);
} else {
BusLayoutModel model = new BusLayoutModel();
model.setSeatType(AbstractItem.TYPE_EMPTY);
mArrayBusSeats.add(model);
}
}

In your example, your recyclerview has 3 viewTypes - TYPE_EDGE, TYPE_CENTER, TYPE_EMPTY i.e, seats are of 3 types. As in your question
Say like if there are 41 seats, but using the above git code it will be showing 33 seats
Infact this is the expected behaviour since your recyclerview consists of a viewtype - TYPE_EMPTY which prints an empty view. So if you give 41 seats only 33 will be visible since the rest of the seats are of type TYPE_EMPTY . If you count the empty positions in your recyclerview as well, then you can see that the total will be 41.

In your Recycler View implementation try to reduce row height, as compared to redbus your row height is almost double.

Have you tried with the number of column 5 ??
I haven't use the library but when I checked it I think I should be done like this
numOfColumns = 5;
mArrayBusSeats = new ArrayList<>();
for (int i = 0; i < mArraySeats.size(); i++) {
BusLayoutModel model = mArraySeats.get(i);
if (i % numOfColumns == 0 || i % numOfColumns == 4) {
model.setSeatType(AbstractItem.TYPE_EDGE);
mArrayBusSeats.add(model);
} else if (i % numOfColumns == 1 || i % numOfColumns == 3) {
model.setSeatType(AbstractItem.TYPE_CENTER);
mArrayBusSeats.add(model);
} else {
model.setSeatType(AbstractItem.TYPE_EMPTY);
mArrayBusSeats.add(model);
}
}

Related

Iterative queue-based flood fill algorithm 'expandToNeighborsWithMap()' function is unusually slow

I am creating a pixel art editor for Android, and as for all pixel art editors, a paint bucket (fill tool) is a must need.
To do this, I did some research on flood fill algorithms online.
I stumbled across the following video which explained how to implement an iterative flood fill algorithm in your code. The code used in the video was JavaScript, but I was easily able to convert the code from the video to Kotlin:
https://www.youtube.com/watch?v=5Bochyn8MMI&t=72s&ab_channel=crayoncode
Here is an excerpt of the JavaScript code from the video:
Converted code:
Tools.FILL_TOOL -> {
val seedColor = instance.rectangles[rectTapped]?.color ?: Color.WHITE
val queue = LinkedList<XYPosition>()
queue.offer(MathExtensions.convertIndexToXYPosition(rectangleData.indexOf(rectTapped), instance.spanCount.toInt()))
val selectedColor = getSelectedColor()
while (queue.isNotEmpty() && seedColor != selectedColor) { // While the queue is not empty the code below will run
val current = queue.poll()
val color = instance.rectangles.toList()[convertXYDataToIndex(instance, current)].second?.color ?: Color.WHITE
if (color != seedColor) {
continue
}
instance.extraCanvas.apply {
instance.rectangles[rectangleData[convertXYDataToIndex(instance, current)]] = defaultRectPaint // Colors in pixel with defaultRectPaint
drawRect(rectangleData[convertXYDataToIndex(instance, current)], defaultRectPaint)
for (index in expandToNeighborsWithMap(instance, current)) {
val candidate = MathExtensions.convertIndexToXYPosition(index, instance.spanCount.toInt())
queue.offer(candidate)
}
}
}
}
Now, I want to address two major issues I'm having with the code of mine:
Performance
Flooding glitch (fixed by suggestion from person in the comments)
Performance
A flood fill needs to be very fast and shouldn't take less than a second, the problem is, say I have a canvas of size 50 x 50, and I decide to fill in the whole canvas, it can take up to 8 seconds or more.
Here is some data I've compiled for the time it's taken to fill in a whole canvas given the spanCount value:
spanCount
approx time taken in seconds to fill whole canvas
10
<1 seconds
20
~2 seconds
40
~6 seconds
60
~15 seconds
100
~115 seconds
The conclusion from the data is that the flood fill algorithm is unusually slow.
To find out why, I decided to test out which parts of the code are taking the most time to compile. I came to the conclusion that the expandToNeighbors function is taking the most time out of all the other tasks:
Here is an excerpt of the expandToNeighbors function:
fun expandToNeighbors(instance: MyCanvasView, from: XYPosition): List<Int> {
var asIndex1 = from.x
var asIndex2 = from.x
var asIndex3 = from.y
var asIndex4 = from.y
if (from.x > 1) {
asIndex1 = xyPositionData!!.indexOf(XYPosition(from.x - 1, from.y))
}
if (from.x < instance.spanCount) {
asIndex2 = xyPositionData!!.indexOf(XYPosition(from.x + 1, from.y))
}
if (from.y > 1) {
asIndex3 = xyPositionData!!.indexOf(XYPosition(from.x, from.y - 1))
}
if (from.y < instance.spanCount) {
asIndex4 = xyPositionData!!.indexOf(XYPosition(from.x, from.y + 1))
}
return listOf(asIndex1, asIndex2, asIndex3, asIndex4)
}
To understand the use of the expandToNeighbors function, I would recommend watching the video that I linked above.
(The if statements are there to make sure you won't get an IndexOutOfBoundsException if you try and expand from the edge of the canvas.)
This function will return the index of the north, south, west, and east pixels from the xyPositionData list which contains XYPosition objects.
(The black pixel is the from parameter.)
The xyPositionData list is initialized once in the convertXYDataToIndex function, here:
var xyPositionData: List<XYPosition>? = null
var rectangleData: List<RectF>? = null
fun convertXYDataToIndex(instance: MyCanvasView, from: XYPosition): Int {
if (rectangleData == null) {
rectangleData = instance.rectangles.keys.toList()
}
if (xyPositionData == null) {
xyPositionData = MathExtensions.convertListOfSizeNToListOfXYPosition(
rectangleData!!.size,
instance.spanCount.toInt()
)
}
return xyPositionData!!.indexOf(from)
}
So, the code works fine (kind of) but the expandToNeighbors function is very slow, and it is the main reason why the flood fill algorithm is taking a long time.
My colleague suggested that indexOf may be slowing everything down, and that I should probably switch to a Map-based implementation with a key being XYPosition and a value being Int representing the index, so I replaced it with the following:
fun expandToNeighborsWithMap(instance: MyCanvasView, from: XYPosition): List<Int> {
var asIndex1 = from.x
var asIndex2 = from.x
var asIndex3 = from.y
var asIndex4 = from.y
if (from.x > 1) {
asIndex1 = rectangleDataMap!![XYPosition(from.x - 1, from.y)]!!
}
if (from.x < instance.spanCount) {
asIndex2 = rectangleDataMap!![XYPosition(from.x + 1, from.y)]!!
}
if (from.y > 1) {
asIndex3 = rectangleDataMap!![XYPosition(from.x, from.y - 1)]!!
}
if (from.y < instance.spanCount) {
asIndex4 = rectangleDataMap!![XYPosition(from.x, from.y + 1)]!!
}
return listOf(asIndex1, asIndex2, asIndex3, asIndex4)
}
It functions the same way, only this time it uses a Map which is initialized here:
var xyPositionData: List<XYPosition>? = null
var rectangleData: List<RectF>? = null
var rectangleDataMap: Map<XYPosition, Int>? = null
fun convertXYDataToIndex(instance: MyCanvasView, from: XYPosition): Int {
if (rectangleData == null) {
rectangleData = instance.rectangles.keys.toList()
}
if (xyPositionData == null) {
xyPositionData = MathExtensions.convertListOfSizeNToListOfXYPosition(
rectangleData!!.size,
instance.spanCount.toInt()
)
}
if (rectangleDataMap == null) {
rectangleDataMap = MathExtensions.convertListToMap(
rectangleData!!.size,
instance.spanCount.toInt()
)
}
return xyPositionData!!.indexOf(from)
}
Converting the code to use a map increased the speed by around 20%, although the algorithm is still slow.
After trying to make the algorithm work faster, I'm out of ideas and I'm unsure why the expandToNeighbors function is taking a long time.
Implementation-wise it is quite messy unfortunately because of the whole list index to XYPosition conversions, but at least it works - the only problem is the performance.
So I have two one major problem.
I've actually pushed the fill tool to GitHub as a KIOL (Known Issue or Limitation), so the user can use the fill tool if they want, but they need to be aware of the limitations/issues. This is so anyone can have a look at my code and reproduce the bugs.
Link to repository:
https://github.com/realtomjoney/PyxlMoose
Edit
I understand that this question is extremely difficult to answer and will require a lot of thinking. I would recommend cloning PyxlMoose and reproduce the errors, then work from there. Relying on the code snippets isn't enough.
Formula for converting XY position to an index
Somebody in the comments suggested a formula for converting an XYPosition to an index value, I came up with the following method which works:
fun convertXYPositionToIndex(xyPosition: XYPosition, spanCount: Int): Int {
val positionX = xyPosition.x
val positionY = xyPosition.y
return (spanCount - positionY) + (spanCount * (positionX - 1))
}
The only problem is - it increases the speed by around 50% but it's still taking around 10-15 seconds to fill in an area of 80 by 80 pixels, so it has helped to a large degree although it's still very slow.
I think the performance issue is because of expandToNeighbors method generates 4 points all the time. It becomes crucial on the border, where you'd better generate 3 (or even 2 on corner) points, so extra point is current position again. So first border point doubles following points count, second one doubles it again (now it's x4) and so on.
If I'm right, you saw not the slow method work, but it was called too often.
How I fixed it:
Getting rid of the toList() calls.
Creating an convertXYPositionToIndex() function.
Here is my new code:
Tools.FILL_TOOL -> {
val seedColor = instance.rectangles[rectTapped]?.color ?: Color.WHITE
val queue = LinkedList<XYPosition>()
val spanCount = instance.spanCount.toInt()
queue.offer(MathExtensions.convertIndexToXYPosition(rectangleData.indexOf(rectTapped), spanCount))
val selectedColor = getSelectedColor()
while (queue.isNotEmpty() && seedColor != selectedColor) {
val current = queue.poll()
val color = instance.rectangles[rectangleData[convertXYDataToIndex(spanCount, current)]]?.color ?: Color.WHITE
if (color != seedColor) {
continue
}
instance.rectangles[rectangleData[convertXYDataToIndex(spanCount, current)]] = defaultRectPaint // Colors in pixel with defaultRectPaint
instance.extraCanvas.drawRect(rectangleData[MathExtensions.convertXYPositionToIndex(current, spanCount)], defaultRectPaint)
for (index in expandToNeighborsWithMap(spanCount, current)) {
val candidate = MathExtensions.convertIndexToXYPosition(index, spanCount)
queue.offer(candidate)
}
}
val timeTakenForThis = (System.currentTimeMillis()-startTime)
totalTime += timeTakenForThis
}
Expand to neighbors func:
fun expandToNeighborsWithMap(spanCount: Int, from: XYPosition): List<Int> {
val toReturn = mutableListOf<Int>()
if (from.x > 1) {
toReturn.add(MathExtensions.convertXYPositionToIndex(XYPosition(from.x - 1, from.y), spanCount))
}
if (from.x < spanCount) {
toReturn.add(MathExtensions.convertXYPositionToIndex(XYPosition(from.x + 1, from.y), spanCount))
}
if (from.y > 1) {
toReturn.add(MathExtensions.convertXYPositionToIndex(XYPosition(from.x, from.y - 1), spanCount))
}
if (from.y < spanCount) {
toReturn.add(MathExtensions.convertXYPositionToIndex(XYPosition(from.x, from.y + 1), spanCount))
}
return toReturn
}
It takes less than a second for canvas sizes of 100 by 100 and 200 by 200, so I'd say it's in the usable stage now.
I would say this is one of the simplest Android flood fill algorithms out there to understand, so if anyone is making an app similar to mine and they want a flood fill tool they can copy my code.
A guy in the comments called EvilTalk helped me with this.

How to print specific numbers from a for loop using Kotlin

So I am fairly new to Kotlin and I need to generate specific numbers from a for loop of 1 to 13.
For the first output I need only odd numbers
For the second output I need numbers 2, 5, 8, 11, 14, 19 and 20 from a for loop of 0 to 20
For starters I can print an entire list using:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
for (i in 1..13){
println(i)
}
}
}
But that's it. What do I need to print the other required outputs?
Once you know how to write a for loop that prints every number, the question becomes how to identify a number that you "should" print from a number that you should not.
Your first sequence is all odd numbers, so #DipankarBaghel's answer covers that. Your second sequence seems to be all numbers for which the remainder when dividing by 3 is 2. (Except 19; did you mean 17 for that one?)
You can use the same operator in this case, but instead of checking for 0 (or for != 0) you can check that the remainder is 2:
for (i in 0..20) {
if (i % 3 == 2) {
println(i)
}
}
The key concept here is that of %, the remainder operator (sometimes called the modulo operator). The result of x % y will be the remainder when x is divided by y. Odd numbers have a remainder of 1 when divided by 2, so i % 2 == 1 will be true only for (positive) odd numbers.
To check even you need to do i%2==0 and for odd just check i%2!=0.
for (i in 1..13){
if(i%2!=0){
println("odd number "+i);
}
if(i%2==0){
println("even number "+i);
}
}
Hope this will help you.
To generate Odd numbers:
for (i in 1..13) {
if(i % 2 == 1 ){
println(i + ", ");
}
}
To Generate 2, 5, 8, 11, 14, 19 and 20:
for (i in 0..20) {
if (i % 3 == 2) {
println(i + ", ");
}
}

Implementing fifo in an array

I have an array and I want to implement FIFO. my project consist of 5 games and a score for each individual game. I had already coded it. heres my code:
if (Game.isNewScore) {
for (int i = 0; i < 5; i++) {
if (addition[i] == 0) {
addition[i] = Game.score;
break;
}
}
}
now my problem is when Im already in the sixth game the scoring should replace the score in the 1st game then transfering the scores from left but it seems the score from the game 6 is not showing.
e.g
2 4 6 8 9 - scores
1 2 3 4 5 - no. of Games
when on sixth game
4 6 8 9 (new score)
can someone teach me on this kind of logic? when you seems not getting my point please tell me. Im badly needing a help. thanks
Your best for this would be to change addition for int[] to ArrayList<Integer>. Your code could look something like this then :
ArrayList<Integer> addition = new ArrayList<>();
/*some other code....
*/
if(Game.isNewScore) {
if (addition.size() == 5){
addition.remove(0);
}
addition.add(Game.score);
}

How to make a realtime rollingwindow graph using MPAndroidChart

[also posted on MPAndroidChart's Github]
I need realtime graph with a rolling windows, that's when I ran into 'problems'. Adding data is no problem, but after adding data with an Xvalue(index) that's higher than the current width of the graph the graph doesn't autoscroll because it don't seem to be able to always display [X] Xvalues.
Example of issue:
The result in graph 3 is not what I want for displaying realtime data. A scrollingwindow is much more usefull. So I tried to archieve this..
My working 'solution' was to remove the first Xvalue, add a new one and move all Xindexes of all Entries on screen one to the left. The result is some code like this:
int GRAPH_WIDTH = 10;
LineData lineData = chart.getData();
LineDataSet lineDataSet = lineData.getDataSetByIndex(0);
int count = lineDataSet.getEntryCount();
// Make rolling window
if (lineData.getXValCount() <= count) {
// Remove/Add XVal
lineData.getXVals().add("" + count);
lineData.getXVals().remove(0);
// Move all entries 1 to the left..
for (int i=0; i < count; i++) {
Entry e = lineDataSet.getEntryForXIndex(i);
if (e==null) continue;
e.setXIndex(e.getXIndex() - 1);
}
// Set correct index to add value
count = GRAPH_WIDTH;
}
// Add new value
lineData.addEntry(new Entry([random value], count), 0);
// Make sure to draw
chart.notifyDataSetChanged();
chart.invalidate();
This works quite well actually (as seen in this video here ), but I feel like there must be an easier way to do this. Maybe I overlooked some API window/scrolling..
But if this is the 'right' way to archieve this result then it would be an enhancement to add support for this kind of graphs in your library.
Thank you for the video.
I am surprised you found a workaround that is rather complicated but works quite well.
Unfortunately this is currently the only way to achieve what you want. I will work on making this easier soon probably reusing some of your code.
Also take a look at these two methods:
setScaleMinima(...)
centerViewPort(...)
I took your code and changed it a bit. It will only show up to GRAPH_WIDTH number of points at a time. Then it scrolls along deleting the older data. Useful if you're only interested in relatively recent data. Is that what you were going for?
public void addTimeEntry() {
String entry_date_time = new SimpleDateFormat("MMM d - HH:mm:ss").format(new Date());
LineData lineData = mChart.getData();
int GRAPH_WIDTH = 15;
if (lineData != null) {
LineDataSet set = lineData.getDataSetByIndex(0);
if (set == null) {
set = createSet();
lineData.addDataSet(set);
}
// Make rolling window
if (lineData.getXValCount() > GRAPH_WIDTH) {
lineData.getXVals().remove(0);
set.removeEntry(0);
lineData.getXVals().add(entry_date_time);
lineData.addEntry(new Entry((float) (Math.random() * 40) + 30f, GRAPH_WIDTH), 0);
// lineData.getXVals().add(entry_date_time);
// Move all entries 1 to the left..
for (int i=0; i < set.getEntryCount(); i++) {
Entry e = set.getEntryForXIndex(i);
if (e==null) continue;
e.setXIndex(e.getXIndex() - 1);
}
}
else{
lineData.getXVals().add(entry_date_time);
lineData.addEntry(new Entry((float) (Math.random() * 40) + 30f, lineData.getXValCount()-1), 0);
}
// let the chart know it's data has changed
mChart.notifyDataSetChanged();
mChart.invalidate();
}
}

How to prevent EditText from breaking a line after punctuation

As default, an Android EditText will break a line if the line is longer than the view, like this:
Thisisalineanditisveryverylongs (end of view)
othisisanotherline
or if the line contains a punctuation character, like this:
Thisisalineanditsnotsolong; (several characters from the end of view)
butthisisanotherline
As a requirement of my work, the text has to break a line only if the line is longer than the view, like this:
Thisisalineanditsnotsolong;andt (end of view)
hisisanotherline
There must be a way to achieve this, am I right? So far I haven't found anyway to do this.
The way TextView (and EditText) breaks the text is through private function calls to BoringLayout internally. So, the best way would be to sublcass EditText and rewrite these functions. But it will not be a trivial task.
So, in the TextView class there are creations of different classes for text style. The one we look is DynamicLayout. In this class we reach to a reference of the class StaticLayout (in a variable called reflowed). In the constructor of this class you will find the text wrap algorithm:
/*
* From the Unicode Line Breaking Algorithm:
* (at least approximately)
*
* .,:; are class IS: breakpoints
* except when adjacent to digits
* / is class SY: a breakpoint
* except when followed by a digit.
* - is class HY: a breakpoint
* except when followed by a digit.
*
* Ideographs are class ID: breakpoints when adjacent,
* except for NS (non-starters), which can be broken
* after but not before.
*/
if (c == ' ' || c == '\t' ||
((c == '.' || c == ',' || c == ':' || c == ';') &&
(j - 1 < here || !Character.isDigit(chs[j - 1 - start])) &&
(j + 1 >= next || !Character.isDigit(chs[j + 1 - start]))) ||
((c == '/' || c == '-') &&
(j + 1 >= next || !Character.isDigit(chs[j + 1 - start]))) ||
(c >= FIRST_CJK && isIdeographic(c, true) &&
j + 1 < next && isIdeographic(chs[j + 1 - start], false))) {
okwidth = w;
ok = j + 1;
Here's where all the wrapping goes. So you will need to subclass take care for StaticLayout, DynamicLayout, TextView and finally EditText which - I am sure - will be a nightmare :( I am not even sure how all the flow goes. If you want - take a look at TextView first and check for getLinesCount calls - this will be the starting point.
This Line Breaking algorithm in Android really sucks, it isn't even logically correct - a comma can not be the last character of a line. it only produces unnecessary line breaks, which results in extremely weird text layout.
Hi Here is one method I first get from another guy and then make a little bit changes, it really works for me you can have a try.
//half ASCII transfer to full ASCII
public static String ToSBC(String input) {
char[] c = input.toCharArray();
for (int i = 0; i< c.length; i++) {
if (c[i] == 32) {
c[i] = (char) 12288;
continue;
}
if (c[i]<=47 && c[i]>32 )
c[i] = (char) (c[i] + 65248);
}
return new String(c);
}
}
here it is. I change some special characters from half corner to full corner, such as "," ".", and the effect is pretty good. You can have try.

Categories

Resources