Why the bind of the width of TableColumn is not working? - binding

I would like to bind the width of the second column of my TableView with the width of my TableView.
My custom TableView :
public class CustomTableView<T> extends
TableView<ObservableList<Pair<String, Object>>> implements Cloneable {
private static Logger logger = Logger.getLogger(CustomTableView.class);
private double prefWidth; // optionnel
private double minWidth; // optionnel
private double maxWidth; // optionnel
private double height; // optionnel
private List<InfoColumnBean> cols; // optionnel
private ObservableList<ObservableList<Pair<String, Object>>> data; // optionnel
private boolean withContextMenu; // optionnel
private ContextMenu menu; // optionnel
private String title; // obligatoire
private int titleWidth; // optionnel
private Label placeHolder; // optionnel
#SuppressWarnings("unchecked")
public CustomTableView(CustomTableViewBuilder<T> builder) {
super();
// apparence tableau
this.prefWidth = builder.prefWidth;
this.minWidth = builder.minWidth;
this.maxWidth = builder.maxWidth;
this.height = builder.height;
this.title = builder.title;
this.titleWidth = builder.titleWidth;
this.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
this.getStyleClass().add("tableView");
this.setPrefWidth(prefWidth);
this.setMinWidth(minWidth);
this.setMaxWidth(maxWidth);
this.setPrefHeight(height);
this.setEditable(false);
DropShadow ds = new DropShadow();
ds.setOffsetY(3.0);
ds.setOffsetX(3.0);
ds.setColor(Color.DARKGRAY);
this.setEffect(ds);
this.withContextMenu = builder.withContextMenu;
this.menu = builder.menu;
this.placeHolder = builder.placeHolder;
if (placeHolder != null)
this.setPlaceholder(placeHolder);
// colonnes du tableau
this.cols = builder.cols;
if (cols != null) {
for (final InfoColumnBean col : cols) {
#SuppressWarnings("rawtypes")
TableColumn ws_col = null;
if (col.isColumnWithImage()) {
ws_col = new TableColumn<ObservableList<Pair<String, Object>>, Object>(
col.getName());
ws_col.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<ObservableList<Pair<String, Object>>, Object>, ObservableValue<Object>>() {
#SuppressWarnings("rawtypes")
public ObservableValue<Object> call(
TableColumn.CellDataFeatures<ObservableList<Pair<String, Object>>, Object> data) {
Object value = data.getValue().get(col.getIndex())
.getValue();
return (value instanceof ObservableValue) ? (ObservableValue) value
: new ReadOnlyObjectWrapper<>(value);
}
});
}
if (!col.isColumnWithImage()) {
ws_col = new TableColumn<ObservableList<Pair<String, Object>>, String>(
col.getName());
if (!col.isColumnWithDecimalsManagement())
ws_col.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<ObservableList<Pair<String, Object>>, String>, ObservableValue<String>>() {
public ObservableValue<String> call(
TableColumn.CellDataFeatures<ObservableList<Pair<String, Object>>, String> data) {
return new ReadOnlyObjectWrapper<>(data
.getValue().get(col.getIndex())
.getKey());
}
});
else {
ws_col.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<ObservableList<Pair<String, Object>>, String>, ObservableValue<String>>() {
public ObservableValue<String> call(
TableColumn.CellDataFeatures<ObservableList<Pair<String, Object>>, String> data) {
// gestion des décimales
NumberFormat df = DecimalFormat.getInstance();
df.setMaximumFractionDigits(2);
df.setMinimumFractionDigits(2);
df.setRoundingMode(RoundingMode.HALF_UP);
String ret = df.format(
Double.parseDouble(data.getValue()
.get(col.getIndex()).getKey()))
.replace(",", ".");
return new ReadOnlyObjectWrapper<>(ret);
}
});
}
}
if (col.getId() != null && !col.getId().equals("")) {
logger.debug("col_name, id = " + col.getName() + " "
+ col.getId());
ws_col.setId(col.getId());
}
ws_col.setPrefWidth(col.getPrefWidth());
ws_col.setMinWidth(col.getMinWidth());
ws_col.setMaxWidth(col.getMaxWidth());
ws_col.getStyleClass().add(col.getStyle());
ws_col.setResizable(col.isResizable());
ws_col.setSortable(col.isSortable());
if (col.getBindPrefWidthWithTable() > 0)
ws_col.prefWidthProperty().bind(
this.widthProperty().subtract(
col.getBindPrefWidthWithTable()));
this.getColumns().add(ws_col);
}
logger.debug("cols size = " + this.getColumns().size());
// données
this.data = builder.data;
if (data != null) {
// style lignes
refresh();
this.setItems(data);
logger.debug("data.size = " + data.size());
} else {
ObservableList<ObservableList<Pair<String, Object>>> items = FXCollections
.observableArrayList();
this.setItems(items);
}
// menu contextuel ?
final CustomTableView<T> current = this;
if (withContextMenu) {
this.addEventHandler(MouseEvent.MOUSE_CLICKED,
new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getButton().equals(
MouseButton.SECONDARY)
&& current.getItems().size() > 0) {
menu.show(current, event.getScreenX(),
event.getScreenY());
}
}
});
}
}
}
/**
* #return titre du contrôle
*
* #since 0.0.8
*/
public String getTitle() {
return title;
}
/**
* #return longueur titre label
*
* #since 0.0.8
*/
public int getTitleWidth() {
return titleWidth;
}
/**
* refresh tableView
*
* #since 0.0.8
*/
public void refresh() {
this.setRowFactory(new Callback<TableView<ObservableList<Pair<String, Object>>>, TableRow<ObservableList<Pair<String, Object>>>>() {
#Override
public TableRow<ObservableList<Pair<String, Object>>> call(
TableView<ObservableList<Pair<String, Object>>> tableView) {
final TableRow<ObservableList<Pair<String, Object>>> row = new TableRow<ObservableList<Pair<String, Object>>>() {
protected void updateItem(
ObservableList<Pair<String, Object>> pair,
boolean empty) {
super.updateItem(pair, empty);
int idx = getIndex();
if (idx >= 0) {
if ((idx % 2) == 0)
getStyleClass().add("ligne1");
else
getStyleClass().add("ligne2");
}
}
};
return row;
}
});
}
#Override
public CustomTableView<T> clone() throws CloneNotSupportedException {
CustomTableView<T> cloneObject = new CustomTableView<T>();
cloneObject.setItems(super.getItems());
return cloneObject;
}
/**
* useful for cloning
*
* #since 0.0.8
*/
public CustomTableView() {
super();
}
/**
*
* Classe : CustomTableViewBuilder
*
* paramètres contrôle
*
* #author fmaupin
*
* #since 0.0.8
*
*/
public static class CustomTableViewBuilder<T> {
private double prefWidth = 200; // optionnel
private double minWidth = 200; // optionnel
private double maxWidth = 200; // optionnel
private double height = 200; // optionnel
private List<InfoColumnBean> cols = null; // optionnel
private ObservableList<ObservableList<Pair<String, Object>>> data = null; // optionnel
private boolean withContextMenu = false; // optionnel
private ContextMenu menu = null; // optionnel
private String title = ""; // obligatoire
private int titleWidth = 0; // optionnel
private Label placeHolder = null; // optionnel
/**
* #param title
* : titre label contrôle
*
* #since 0.0.8
*/
public CustomTableViewBuilder(String title) {
this.title = title;
}
/**
*
* longueur titre contrôle
*
* #param width
* : longueur titre
*
* #since 0.0.8
*/
public CustomTableViewBuilder<T> titleWidth(int width) {
this.titleWidth = width;
return this;
}
/**
*
* placeholder
*
* #param value
* : message pour placeholder
*
* #since 0.0.8
*/
public CustomTableViewBuilder<T> setPlaceHolder(String value) {
this.placeHolder = new Label(value);
return this;
}
/**
*
* largeurs tableau
*
* #param width
* : toutes largeurs tableau
*
* #since 0.0.8
*/
public CustomTableViewBuilder<T> with(double width) {
this.prefWidth = width;
this.minWidth = width;
this.maxWidth = width;
return this;
}
/**
*
* largeur tableau
*
* #param width
* : largeur tableau
*
* #since 0.0.8
*/
public CustomTableViewBuilder<T> prefWith(double width) {
this.prefWidth = width;
return this;
}
/**
*
* largeur minimale tableau
*
* #param width
* : largeur tableau
*
* #since 0.0.8
*/
public CustomTableViewBuilder<T> minWith(double width) {
this.minWidth = width;
return this;
}
/**
*
* largeur maximale tableau
*
* #param width
* : largeur tableau
*
* #since 0.0.8
*/
public CustomTableViewBuilder<T> maxWith(double width) {
this.maxWidth = width;
return this;
}
/**
*
* hauteur tableau
*
* #param height
* : hauteur tableau
*
* #since 0.0.8
*/
public CustomTableViewBuilder<T> height(double height) {
this.height = height;
return this;
}
/**
*
* ajouter colonne(s) dans tableau
*
* #param infoColumns
* : informations sur colonnes (nom, index, largeur, largeur
* minimum, largeur maximum, style)
*
* #since 0.0.8
*/
public CustomTableViewBuilder<T> addColumns(
List<InfoColumnBean> infoColumns) {
this.cols = infoColumns;
return this;
}
/**
*
* ajouter données dans tableau
*
* #param data
* : données à ajouter
*
* #since 0.0.8
*/
public CustomTableViewBuilder<T> addDATA(
ObservableList<ObservableList<Pair<String, Object>>> data) {
this.data = data;
return this;
}
/**
*
* avec menu contextuel ?
*
* #param menu
* : descriptif menu contextuel
*
* #since 0.0.8
*/
public CustomTableViewBuilder<T> withContextMenu(ContextMenu menu) {
this.withContextMenu = true;
this.menu = menu;
return this;
}
/**
* construire contrôle
*
* #since 0.0.8
*/
public CustomTableView<T> build() {
return new CustomTableView<T>(this);
}
}
}
The initialisation of my custom TableView (InfoColumns contains description of columns):
mytable = new CustomTableView.CustomTableViewBuilder<ObservableList<Pair<String, Object>>>(
"")
.setPlaceHolder(
"my message !")
.addColumns(infoColumns).prefWith(width * 0.4f)
.minWith(TABLEVIEW_WIDTH).maxWith(width).height(height * 0.75)
.build();
And my Binding :
mytable.getColumns().get(1).prefWidthProperty().bind(mytable.widthProperty().subtract(385));
Unfortunately it does not work !
If you have a idea ...
Thanks you in advance
Fabrice

Well it does work, follow the example below :
import javafx.application.Application;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class TableViewColumnBind extends Application {
#Override
public void start(Stage stage) throws Exception {
BorderPane borderPane = new BorderPane();
Scene scene = new Scene(borderPane, 500, 500);
String cssFile = getClass().getResource("tableviewgridline.css").toExternalForm();
scene.getStylesheets().add(cssFile);
stage.setScene(scene);
TableView<Integer> table = new TableView<>();
ObservableList<Integer> data = FXCollections.observableArrayList();
for (int i = 0; i < 20; i++) {
data.add(i);
}
table.setItems(data);
for (int i = 0; i < 10; i++) {
TableColumn<Integer,String> column = new TableColumn<>(Integer.toString(i));
column.setCellValueFactory(param -> new ReadOnlyObjectWrapper<>(param.getValue().toString()));
table.getColumns().add(column);
}
table.getColumns().get(1).prefWidthProperty().bind(table.widthProperty().subtract(400));
borderPane.setCenter(table);
stage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}

Related

How spring data elasticsearch use offset and limit to query

How spring data elastisearch use offset and limit to query. I want to use offset and limit param to query page.But I can not find methods support. For Example:
queryBuild.withPageable(PageRequest.of(pageIndex, pageSize));
Page<Content> content = elasticsearchOperations.queryForPage(queryBuild.build(),Content.class);
it could be ok
But I can not found method with offset and limit
queryBuild.withPageable(PageRequest.of(offset, limit));
I have the same problem, so I implemented the following class
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
public class OffsetLimitPageable implements Pageable {
private int offset;
private int page;
private int size;
private Sort sort = Sort.unsorted();
protected OffsetLimitPageable(int offset, int page, int size) {
if (offset < 0) {
throw new IllegalArgumentException("Offset must not be less than zero!");
}
if (page < 0) {
throw new IllegalArgumentException("Page index must not be less than zero!");
}
if (size < 1) {
throw new IllegalArgumentException("Page size must not be less than one!");
}
this.offset = offset;
this.page = page;
this.size = size;
}
public static OffsetLimitPageable of(int offset, int page, int size) {
return new OffsetLimitPageable(offset, page, size);
}
/*
* (non-Javadoc)
* #see org.springframework.data.domain.Pageable#getPageNumber()
*/
#Override
public int getPageNumber() {
return page;
}
/*
* (non-Javadoc)
* #see org.springframework.data.domain.Pageable#getPageSize()
*/
#Override
public int getPageSize() {
return size;
}
/*
* (non-Javadoc)
* #see org.springframework.data.domain.Pageable#getOffset()
*/
#Override
public long getOffset() {
return offset + page * size;
}
/*
* (non-Javadoc)
* #see org.springframework.data.domain.Pageable#getSort()
*/
#Override
public Sort getSort() {
return sort;
}
/*
* (non-Javadoc)
* #see org.springframework.data.domain.Pageable#next()
*/
public Pageable next() {
return of(offset, page + 1, size);
}
/*
* (non-Javadoc)
* #see org.springframework.data.domain.Pageable#previousOrFirst()
*/
public Pageable previousOrFirst() {
return hasPrevious() ? of(offset, page - 1, size) : first();
}
/*
* (non-Javadoc)
* #see org.springframework.data.domain.Pageable#first()
*/
public Pageable first() {
return of(offset, 0, size);
}
/*
* (non-Javadoc)
* #see org.springframework.data.domain.Pageable#hasPrevious()
*/
public boolean hasPrevious() {
return page > 0;
}
}
And, use like this
queryBuild.withPageable(OffsetLimitPageable.of(offset, page, limit));
This is not supported in spring-data-es (or in ANY spring-data project), so you'll have to provide your own custom implementation for the Pageable interface
Take a look here or here and here if you attempt to use the repository variant (extending ElasticsearchRepository<...,...>) and implement your own.
Then perform the query just as you noted, with
PageRequest p = new MyOwnPageRequest(offset, limit);
SearchQuery sq = new NativeSearchQueryBuilder()
.withQuery(matchAllQuery())
.withPageable(p)
.build();
Page<SampleEntity> result = elasticsearchTemplate.queryForPage(sq, SampleEntity.class);

API-platform POST operation swagger page empty

Symfony 3.4.10
Api-platform/core
I set up API-Platform/core on a Symfony 3.4.10 project, everything work well and I can access the swagger page for test via "/api" (url route to show api test interface).
For the GET operations everything is right.
But for the POST operation on an entity named "Trajet" (see code) the "example value" show only "{}" and nothing else. If I try the button "Try it out" I get a blank space where nothing is shew instead of the traditional form to test a post operation (see capture).
capture of the swagger page:
I don't understand what I missed. Could someone please give me ideas how to fix it and where the problem can come from. I'm out of ideas to find a solution.
Here is the code of my entity Trajet for which post operation is empty and seem to be not usable.
<?php
namespace Covoituragesimple\AnnonceBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* Trajet
* #ApiResource(
* collectionOperations={"get", "post"},
* itemOperations={"get"},
* attributes={
* "normalization_context"={"groups"={"read"}},
* "denormalization_context"={"groups"={"write"}}
* }
* )
* #ORM\Table(name="trajet")
* #ORM\Entity(repositoryClass="Covoituragesimple\AnnonceBundle\Repository\TrajetRepository")
*/
class Trajet
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #Groups({"read"})
*/
private $id;
/**
* #var int
*
* #ORM\Column(name="numberofseats", type="integer")
* #Assert\NotBlank()
* #Groups({"read"})
*/
private $numberofseats;
/**
* #var \DateTime
*
* #ORM\Column(name="creationdate", type="datetime")
* #Assert\Type("\DateTime")
* #Groups({"read"})
*/
private $creationdate;
/**
* #var \DateTime
*
* #ORM\Column(name="rdvdatetime", type="datetime")
* #Assert\Type("\DateTime")
* #Groups({"read"})
*/
private $rdvdatetime;
/**
* #ORM\ManyToOne(targetEntity="Covoituragesimple\AnnonceBundle\Entity\Commune")
*/
private $commune;
/**
* #var string
*
* #ORM\Column(name="chercheoupropose", type="text")
* #Groups({"read"})
*/
private $chercheoupropose;
/**
* #var string
*
* #ORM\Column(name="message", type="text")
* #Assert\NotBlank(
* message="Un petit message pour votre annonce"
* )
* #Groups({"read"})
*/
private $message;
/**
* #var string
*
* #ORM\Column(name="contrepartietype", type="text")
* #Groups({"read"})
*/
private $contrepartietype;
/**
* #ORM\ManyToOne(targetEntity="Covoituragesimple\AnnonceBundle\Entity\Module")
* #Groups({"read"})
*/
protected $module;
/**
* #var string
*
* #ORM\Column(name="contrepartiemsg", nullable=true, type="text")
* #Groups({"read"})
*/
private $contrepartiemsg;
/**
* #ORM\ManyToOne(targetEntity="Covoituragesimple\AnnonceBundle\Entity\Covoitureur",
inversedBy="trajets",
cascade={"persist"})
*/
protected $covoitureur;
/**
* #ORM\OneToMany(targetEntity="Covoituragesimple\AnnonceBundle\Entity\Contact", mappedBy="trajet", cascade={"remove"})
*/
private $contacts;
/**
* #var bool
*
* #ORM\Column(name="moderated", type="boolean")
*/
private $moderated = false;
/**
* #var bool
*
* #ORM\Column(name="active", type="boolean")
*/
private $active = true;
/**
* Constructor
*/
public function __construct()
{
$this->setNumberofseats(1);
$this->setMessage("");
$this->setRdvdatetime(new \DateTime('today'));
$this->setCreationdate(new \DateTime('now'));
$this->contacts = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id.
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set numberofseats.
*
* #param int $numberofseats
*
* #return Trajet
*/
public function setNumberofseats($numberofseats)
{
$this->numberofseats = $numberofseats;
return $this;
}
/**
* Get numberofseats.
*
* #return int
*/
public function getNumberofseats()
{
return $this->numberofseats;
}
/**
* Set creationdate.
*
* #param \DateTime $creationdate
*
* #return Trajet
*/
public function setCreationdate($creationdate)
{
$this->creationdate = $creationdate;
return $this;
}
/**
* Get creationdate.
*
* #return \DateTime
*/
public function getCreationdate()
{
return $this->creationdate;
}
/**
* Set rdvdatetime.
*
* #param \DateTime $rdvdatetime
*
* #return Trajet
*/
public function setRdvdatetime($rdvdatetime)
{
$this->rdvdatetime = $rdvdatetime;
return $this;
}
/**
* Get rdvdatetime.
*
* #return \DateTime
*/
public function getRdvdatetime()
{
return $this->rdvdatetime;
}
/**
* Set chercheoupropose.
*
* #param string $chercheoupropose
*
* #return Trajet
*/
public function setChercheoupropose($chercheoupropose)
{
$this->chercheoupropose = $chercheoupropose;
return $this;
}
/**
* Get chercheoupropose.
*
* #return string
*/
public function getChercheoupropose()
{
return $this->chercheoupropose;
}
/**
* Set message.
*
* #param string $message
*
* #return Trajet
*/
public function setMessage($message)
{
$this->message = $message;
return $this;
}
/**
* Get message.
*
* #return string
*/
public function getMessage()
{
return $this->message;
}
/**
* Set contrepartietype.
*
* #param string $contrepartietype
*
* #return Trajet
*/
public function setContrepartietype($contrepartietype)
{
$this->contrepartietype = $contrepartietype;
return $this;
}
/**
* Get contrepartietype.
*
* #return string
*/
public function getContrepartietype()
{
return $this->contrepartietype;
}
/**
* Set contrepartiemsg.
*
* #param string|null $contrepartiemsg
*
* #return Trajet
*/
public function setContrepartiemsg($contrepartiemsg = null)
{
$this->contrepartiemsg = $contrepartiemsg;
return $this;
}
/**
* Get contrepartiemsg.
*
* #return string|null
*/
public function getContrepartiemsg()
{
return $this->contrepartiemsg;
}
/**
* Set moderated.
*
* #param bool $moderated
*
* #return Trajet
*/
public function setModerated($moderated)
{
$this->moderated = $moderated;
return $this;
}
/**
* Get moderated.
*
* #return bool
*/
public function getModerated()
{
return $this->moderated;
}
/**
* Set active.
*
* #param bool $active
*
* #return Trajet
*/
public function setActive($active)
{
$this->active = $active;
return $this;
}
/**
* Get active.
*
* #return bool
*/
public function getActive()
{
return $this->active;
}
/**
* Set module.
*
* #param \Covoituragesimple\AnnonceBundle\Entity\Module|null $module
*
* #return Trajet
*/
public function setModule(\Covoituragesimple\AnnonceBundle\Entity\Module $module = null)
{
$this->module = $module;
return $this;
}
/**
* Get module.
*
* #return \Covoituragesimple\AnnonceBundle\Entity\Module|null
*/
public function getModule()
{
return $this->module;
}
/**
* Set covoitureur.
*
* #param \Covoituragesimple\AnnonceBundle\Entity\Covoitureur|null $covoitureur
*
* #return Trajet
*/
public function setCovoitureur(\Covoituragesimple\AnnonceBundle\Entity\Covoitureur $covoitureur = null)
{
$this->covoitureur = $covoitureur;
return $this;
}
/**
* Get covoitureur.
*
* #return \Covoituragesimple\AnnonceBundle\Entity\Covoitureur|null
*/
public function getCovoitureur()
{
return $this->covoitureur;
}
/**
* Add contact.
*
* #param \Covoituragesimple\AnnonceBundle\Entity\Contact $contact
*
* #return Trajet
*/
public function addContact(\Covoituragesimple\AnnonceBundle\Entity\Contact $contact)
{
$this->contacts[] = $contact;
return $this;
}
/**
* Remove contact.
*
* #param \Covoituragesimple\AnnonceBundle\Entity\Contact $contact
*
* #return boolean TRUE if this collection contained the specified element, FALSE otherwise.
*/
public function removeContact(\Covoituragesimple\AnnonceBundle\Entity\Contact $contact)
{
return $this->contacts->removeElement($contact);
}
/**
* Get contacts.
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getContacts()
{
return $this->contacts;
}
/**
* Set commune.
*
* #param \Covoituragesimple\AnnonceBundle\Entity\Commune|null $commune
*
* #return Trajet
*/
public function setCommune(\Covoituragesimple\AnnonceBundle\Entity\Commune $commune = null)
{
$this->commune = $commune;
return $this;
}
/**
* Get commune.
*
* #return \Covoituragesimple\AnnonceBundle\Entity\Commune|null
*/
public function getCommune()
{
return $this->commune;
}
}
I think it's because you forget to define "write" group in your entity attribute, if you rename it by "read" in denormalization_context (or add "write" group in your entity attributes), it should work.
Something like this:
* #ApiResource(
* collectionOperations={"get", "post"},
* itemOperations={"get"},
* attributes={
* "normalization_context"={"groups"={"read"}},
* "denormalization_context"={"groups"={"read"}} // <-- here
* }
* )
More detail here: https://api-platform.com/docs/core/serialization
I hope this can help you

How to implement ViewPagers with Dot Sliders in Xamarin.Android?

I am learning Xamarin.Android recently, I was trying to implement pageviewers with dot sliders for my practice from the last few days but failed to implement it, I had refereed many solutions from popular hubs like stackoverflow and github but failed to do so.
Can any one please explain me how to implement the viewpagers with dot sliders in xamarin.android.
I am attaching a photo to this for your reference, which would give you a clear view on my requirement.
as you can see he is sliding the pages using the dot sliders i was trying to do so.
Please explain me how to do it in Xamarin.Android (Native)
First define your own custom circle page-indicator let it be CirclePageIndicator.cs add this to your project (not activity file)
using Android.Content;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Graphics;
using Android.Support.V4.View;
using Android.Util;
using Java.Lang;
using Java.Interop;
namespace MyApplication.Droid.Library
{
public class CirclePageIndicator : View,PageIndicator
{
const int HORIZONTAL = 0;
const int VERTICAL = 1;
private float mRadius;
private Paint mPaintPageFill;
private Paint mPaintStroke;
private Paint mPaintFill;
private ViewPager mViewPager;
private ViewPager.IOnPageChangeListener mListener;
private int mCurrentPage;
private int mSnapPage;
private int mCurrentOffset;
private int mScrollState;
private int mPageSize;
private int mOrientation;
private bool mCentered;
private bool mSnap;
private const int INVALID_POINTER = -1;
private int mTouchSlop;
private float mLastMotionX = -1;
private int mActivePointerId = INVALID_POINTER;
private bool mIsDragging;
public CirclePageIndicator(Context context) : this(context, null)
{
}
public CirclePageIndicator(Context context, IAttributeSet attrs) : this(context, attrs, Resource.Attribute.vpiCirclePageIndicatorStyle)
{
}
public CirclePageIndicator(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
{
//Load defaults from resources
var res = Resources;
int defaultPageColor = res.GetColor(Resource.Color.default_circle_indicator_page_color);
int defaultFillColor = res.GetColor(Resource.Color.default_circle_indicator_fill_color);
int defaultOrientation = res.GetInteger(Resource.Integer.default_circle_indicator_orientation);
int defaultStrokeColor = res.GetColor(Resource.Color.default_circle_indicator_stroke_color);
float defaultStrokeWidth = res.GetDimension(Resource.Dimension.default_circle_indicator_stroke_width);
float defaultRadius = res.GetDimension(Resource.Dimension.default_circle_indicator_radius);
bool defaultCentered = res.GetBoolean(Resource.Boolean.default_circle_indicator_centered);
bool defaultSnap = res.GetBoolean(Resource.Boolean.default_circle_indicator_snap);
//Retrieve styles attributes
var a = context.ObtainStyledAttributes(attrs, Resource.Styleable.CirclePageIndicator, defStyle, Resource.Style.Widget_CirclePageIndicator);
mCentered = a.GetBoolean(Resource.Styleable.CirclePageIndicator_centered, defaultCentered);
mOrientation = a.GetInt(Resource.Styleable.CirclePageIndicator_orientation, defaultOrientation);
mPaintPageFill = new Paint(PaintFlags.AntiAlias);
mPaintPageFill.SetStyle(Paint.Style.Fill);
mPaintPageFill.Color = a.GetColor(Resource.Styleable.CirclePageIndicator_pageColor, defaultPageColor);
mPaintStroke = new Paint(PaintFlags.AntiAlias);
mPaintStroke.SetStyle(Paint.Style.Stroke);
mPaintStroke.Color = a.GetColor(Resource.Styleable.CirclePageIndicator_strokeColor, defaultStrokeColor);
mPaintStroke.StrokeWidth = a.GetDimension(Resource.Styleable.CirclePageIndicator_strokeWidth, defaultStrokeWidth);
mPaintFill = new Paint(PaintFlags.AntiAlias);
mPaintFill.SetStyle(Paint.Style.Fill);
mPaintFill.Color = a.GetColor(Resource.Styleable.CirclePageIndicator_fillColor, defaultFillColor);
mRadius = a.GetDimension(Resource.Styleable.CirclePageIndicator_radius, defaultRadius);
mSnap = a.GetBoolean(Resource.Styleable.CirclePageIndicator_snap, defaultSnap);
a.Recycle();
var configuration = ViewConfiguration.Get(context);
mTouchSlop = ViewConfigurationCompat.GetScaledPagingTouchSlop(configuration);
}
public void SetCentered(bool centered)
{
mCentered = centered;
Invalidate();
}
public bool IsCentered()
{
return mCentered;
}
public void SetPageColor(Color pageColor)
{
mPaintPageFill.Color = pageColor;
Invalidate();
}
public int GetPageColor()
{
return mPaintPageFill.Color;
}
public void SetFillColor(Color fillColor)
{
mPaintFill.Color = fillColor;
Invalidate();
}
public int GetFillColor()
{
return mPaintFill.Color;
}
public void setOrientation(int orientation)
{
switch (orientation)
{
case HORIZONTAL:
case VERTICAL:
mOrientation = orientation;
UpdatePageSize();
RequestLayout();
break;
default:
throw new IllegalArgumentException("Orientation must be either HORIZONTAL or VERTICAL.");
}
}
public int GetOrientation()
{
return mOrientation;
}
public void SetStrokeColor(Color strokeColor)
{
mPaintStroke.Color = strokeColor;
Invalidate();
}
public int GetStrokeColor()
{
return mPaintStroke.Color;
}
public void SetStrokeWidth(float strokeWidth)
{
mPaintStroke.StrokeWidth = strokeWidth;
Invalidate();
}
public float GetStrokeWidth()
{
return mPaintStroke.StrokeWidth;
}
public void SetRadius(float radius)
{
mRadius = radius;
Invalidate();
}
public float GetRadius()
{
return mRadius;
}
public void SetSnap(bool snap)
{
mSnap = snap;
Invalidate();
}
public bool IsSnap()
{
return mSnap;
}
protected override void OnDraw(Canvas canvas)
{
base.OnDraw(canvas);
if (mViewPager == null)
{
return;
}
int count = mViewPager.Adapter.Count;
if (count == 0)
{
return;
}
if (mCurrentPage >= count)
{
SetCurrentItem(count - 1);
return;
}
int longSize;
int longPaddingBefore;
int longPaddingAfter;
int shortPaddingBefore;
if (mOrientation == HORIZONTAL)
{
longSize = Width;
longPaddingBefore = PaddingLeft;
longPaddingAfter = PaddingRight;
shortPaddingBefore = PaddingTop;
}
else
{
longSize = Height;
longPaddingBefore = PaddingTop;
longPaddingAfter = PaddingBottom;
shortPaddingBefore = PaddingLeft;
}
float threeRadius = mRadius * 3;
float shortOffset = shortPaddingBefore + mRadius;
float longOffset = longPaddingBefore + mRadius;
if (mCentered)
{
longOffset += ((longSize - longPaddingBefore - longPaddingAfter) / 2.0f) - ((count * threeRadius) / 2.0f);
}
float dX;
float dY;
float pageFillRadius = mRadius;
if (mPaintStroke.StrokeWidth > 0)
{
pageFillRadius -= mPaintStroke.StrokeWidth / 2.0f;
}
//Draw stroked circles
for (int iLoop = 0; iLoop < count; iLoop++)
{
float drawLong = longOffset + (iLoop * threeRadius);
if (mOrientation == HORIZONTAL)
{
dX = drawLong;
dY = shortOffset;
}
else
{
dX = shortOffset;
dY = drawLong;
}
// Only paint fill if not completely transparent
if (mPaintPageFill.Alpha > 0)
{
canvas.DrawCircle(dX, dY, pageFillRadius, mPaintPageFill);
}
// Only paint stroke if a stroke width was non-zero
if (pageFillRadius != mRadius)
{
canvas.DrawCircle(dX, dY, mRadius, mPaintStroke);
}
}
//Draw the filled circle according to the current scroll
float cx = (mSnap ? mSnapPage : mCurrentPage) * threeRadius;
if (!mSnap && (mPageSize != 0))
{
cx += (mCurrentOffset * 1.0f / mPageSize) * threeRadius;
}
if (mOrientation == HORIZONTAL)
{
dX = longOffset + cx;
dY = shortOffset;
}
else
{
dX = shortOffset;
dY = longOffset + cx;
}
canvas.DrawCircle(dX, dY, mRadius, mPaintFill);
}
public override bool OnTouchEvent(MotionEvent ev)
{
if (base.OnTouchEvent(ev))
{
return true;
}
if ((mViewPager == null) || (mViewPager.Adapter.Count == 0))
{
return false;
}
var action = ev.Action;
switch ((int)action & MotionEventCompat.ActionMask)
{
case (int)MotionEventActions.Down:
mActivePointerId = MotionEventCompat.GetPointerId(ev, 0);
mLastMotionX = ev.GetX();
break;
case (int)MotionEventActions.Move:
{
int activePointerIndex = MotionEventCompat.FindPointerIndex(ev, mActivePointerId);
float x = MotionEventCompat.GetX(ev, activePointerIndex);
float deltaX = x - mLastMotionX;
if (!mIsDragging)
{
if (Java.Lang.Math.Abs(deltaX) > mTouchSlop)
{
mIsDragging = true;
}
}
if (mIsDragging)
{
if (!mViewPager.IsFakeDragging)
{
mViewPager.BeginFakeDrag();
}
mLastMotionX = x;
mViewPager.FakeDragBy(deltaX);
}
break;
}
case (int)MotionEventActions.Cancel:
case (int)MotionEventActions.Up:
if (!mIsDragging)
{
int count = mViewPager.Adapter.Count;
int width = Width;
float halfWidth = width / 2f;
float sixthWidth = width / 6f;
if ((mCurrentPage > 0) && (ev.GetX() < halfWidth - sixthWidth))
{
mViewPager.CurrentItem = mCurrentPage - 1;
return true;
}
else if ((mCurrentPage < count - 1) && (ev.GetX() > halfWidth + sixthWidth))
{
mViewPager.CurrentItem = mCurrentPage + 1;
return true;
}
}
mIsDragging = false;
mActivePointerId = INVALID_POINTER;
if (mViewPager.IsFakeDragging)
mViewPager.EndFakeDrag();
break;
case MotionEventCompat.ActionPointerDown:
{
int index = MotionEventCompat.GetActionIndex(ev);
float x = MotionEventCompat.GetX(ev, index);
mLastMotionX = x;
mActivePointerId = MotionEventCompat.GetPointerId(ev, index);
break;
}
case MotionEventCompat.ActionPointerUp:
int pointerIndex = MotionEventCompat.GetActionIndex(ev);
int pointerId = MotionEventCompat.GetPointerId(ev, pointerIndex);
if (pointerId == mActivePointerId)
{
int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mActivePointerId = MotionEventCompat.GetPointerId(ev, newPointerIndex);
}
mLastMotionX = MotionEventCompat.GetX(ev, MotionEventCompat.FindPointerIndex(ev, mActivePointerId));
break;
}
return true;
}
public void SetViewPager(ViewPager view)
{
if (view.Adapter == null)
{
throw new IllegalStateException("ViewPager does not have adapter instance.");
}
mViewPager = view;
mViewPager.SetOnPageChangeListener(this);
UpdatePageSize();
Invalidate();
}
private void UpdatePageSize()
{
if (mViewPager != null)
{
mPageSize = (mOrientation == HORIZONTAL) ? mViewPager.Width : mViewPager.Height;
}
}
public void SetViewPager(ViewPager view, int initialPosition)
{
SetViewPager(view);
SetCurrentItem(initialPosition);
}
public void SetCurrentItem(int item)
{
if (mViewPager == null)
{
throw new IllegalStateException("ViewPager has not been bound.");
}
mViewPager.CurrentItem = item;
mCurrentPage = item;
Invalidate();
}
public void NotifyDataSetChanged()
{
Invalidate();
}
public void OnPageScrollStateChanged(int state)
{
mScrollState = state;
if (mListener != null)
{
mListener.OnPageScrollStateChanged(state);
}
}
public void OnPageScrolled(int position, float positionOffset, int positionOffsetPixels)
{
mCurrentPage = position;
mCurrentOffset = positionOffsetPixels;
UpdatePageSize();
Invalidate();
if (mListener != null)
{
mListener.OnPageScrolled(position, positionOffset, positionOffsetPixels);
}
}
public void OnPageSelected(int position)
{
if (mSnap || mScrollState == ViewPager.ScrollStateIdle)
{
mCurrentPage = position;
mSnapPage = position;
Invalidate();
}
if (mListener != null)
{
mListener.OnPageSelected(position);
}
}
public void SetOnPageChangeListener(ViewPager.IOnPageChangeListener listener)
{
mListener = listener;
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
if (mOrientation == HORIZONTAL)
{
SetMeasuredDimension(MeasureLong(widthMeasureSpec), MeasureShort(heightMeasureSpec));
}
else
{
SetMeasuredDimension(MeasureShort(widthMeasureSpec), MeasureLong(heightMeasureSpec));
}
}
/**
* Determines the width of this view
*
* #param measureSpec
* A measureSpec packed into an int
* #return The width of the view, honoring constraints from measureSpec
*/
private int MeasureLong(int measureSpec)
{
int result = 0;
var specMode = MeasureSpec.GetMode(measureSpec);
var specSize = MeasureSpec.GetSize(measureSpec);
if ((specMode == MeasureSpecMode.Exactly) || (mViewPager == null))
{
//We were told how big to be
result = specSize;
}
else
{
//Calculate the width according the views count
int count = mViewPager.Adapter.Count;
result = (int)(PaddingLeft + PaddingRight
+ (count * 2 * mRadius) + (count - 1) * mRadius + 1);
//Respect AT_MOST value if that was what is called for by measureSpec
if (specMode == MeasureSpecMode.AtMost)
{
result = Java.Lang.Math.Min(result, specSize);
}
}
return result;
}
/**
* Determines the height of this view
*
* #param measureSpec
* A measureSpec packed into an int
* #return The height of the view, honoring constraints from measureSpec
*/
private int MeasureShort(int measureSpec)
{
int result = 0;
var specMode = MeasureSpec.GetMode(measureSpec);
var specSize = MeasureSpec.GetSize(measureSpec);
if (specMode == MeasureSpecMode.Exactly)
{
//We were told how big to be
result = specSize;
}
else
{
//Measure the height
result = (int)(2 * mRadius + PaddingTop + PaddingBottom + 1);
//Respect AT_MOST value if that was what is called for by measureSpec
if (specMode == MeasureSpecMode.AtMost)
{
result = Java.Lang.Math.Min(result, specSize);
}
}
return result;
}
protected override void OnRestoreInstanceState(IParcelable state)
{
try
{
SavedState savedState = (SavedState)state;
base.OnRestoreInstanceState(savedState.SuperState);
mCurrentPage = savedState.CurrentPage;
mSnapPage = savedState.CurrentPage;
}
catch
{
base.OnRestoreInstanceState(state);
// Ignore, this needs to support IParcelable...
}
RequestLayout();
}
protected override IParcelable OnSaveInstanceState()
{
var superState = base.OnSaveInstanceState();
var savedState = new SavedState(superState);
savedState.CurrentPage = mCurrentPage;
return savedState;
}
public class SavedState : BaseSavedState
{
public int CurrentPage { get; set; }
public SavedState(IParcelable superState) : base(superState)
{
}
private SavedState(Parcel parcel) : base(parcel)
{
CurrentPage = parcel.ReadInt();
}
public override void WriteToParcel(Parcel dest, ParcelableWriteFlags flags)
{
base.WriteToParcel(dest, flags);
dest.WriteInt(CurrentPage);
}
[ExportField("CREATOR")]
static SavedStateCreator InitializeCreator()
{
return new SavedStateCreator();
}
class SavedStateCreator : Java.Lang.Object, IParcelableCreator
{
public Java.Lang.Object CreateFromParcel(Parcel source)
{
return new SavedState(source);
}
public Java.Lang.Object[] NewArray(int size)
{
return new SavedState[size];
}
}
}
}
}
Now add PageIndicator.cs file to your project (not activity file)
using Android.Support.V4.View;
namespace MyApplication.Droid.Library
{
public interface PageIndicator : ViewPager.IOnPageChangeListener
{
/**
* Bind the indicator to a ViewPager.
*
* #param view
*/
void SetViewPager(ViewPager view);
/**
* Bind the indicator to a ViewPager.
*
* #param view
* #param initialPosition
*/
void SetViewPager(ViewPager view, int initialPosition);
/**
* <p>Set the current page of both the ViewPager and indicator.</p>
*
* <p>This <strong>must</strong> be used if you need to set the page before
* the views are drawn on screen (e.g., default start page).</p>
*
* #param item
*/
void SetCurrentItem(int item);
/**
* Set a page change listener which will receive forwarded events.
*
* #param listener
*/
void SetOnPageChangeListener(ViewPager.IOnPageChangeListener listener);
/**
* Notify the indicator that the fragment list has changed.
*/
void NotifyDataSetChanged();
}
}
Now add the following files to your values file:
vpi__styles.xml
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<style name="Widget"></style>
<style name="Widget.CirclePageIndicator" parent="Widget">
<item name="centered">#bool/default_circle_indicator_centered</item>
<item name="fillColor">#color/default_circle_indicator_fill_color</item>
<item name="pageColor">#color/default_circle_indicator_page_color</item>
<item name="orientation">#integer/default_circle_indicator_orientation</item>
<item name="radius">#dimen/default_circle_indicator_radius</item>
<item name="snap">#bool/default_circle_indicator_snap</item>
<item name="strokeColor">#color/default_circle_indicator_stroke_color</item>
<item name="strokeWidth">#dimen/default_circle_indicator_stroke_width</item>
</style>
</resources>
vpi__defaults.xml
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<bool name="default_circle_indicator_centered">true</bool>
<color name="default_circle_indicator_fill_color">#FFFFFFFF</color>
<color name="default_circle_indicator_page_color">#00000000</color>
<integer name="default_circle_indicator_orientation">0</integer>
<dimen name="default_circle_indicator_radius">3dp</dimen>
<bool name="default_circle_indicator_snap">false</bool>
<color name="default_circle_indicator_stroke_color">#FFDDDDDD</color>
<dimen name="default_circle_indicator_stroke_width">1dp</dimen>
</resources>
vpi__colors.xml
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<color name="vpi__background_holo_dark">#ff000000</color>
<color name="vpi__background_holo_light">#fff3f3f3</color>
<color name="vpi__bright_foreground_disabled_holo_dark">#ff4c4c4c</color>
<color name="vpi__bright_foreground_disabled_holo_light">#ffb2b2b2</color>
</resources>
vpi__attrs.xml
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<declare-styleable name="ViewPagerIndicator">
<!-- Style of the circle indicator. -->
<attr name="vpiCirclePageIndicatorStyle" format="reference"/>
</declare-styleable>
<declare-styleable name="CirclePageIndicator">
<!-- Whether or not the indicators should be centered. -->
<attr name="centered" format="boolean" />
<!-- Color of the filled circle that represents the current page. -->
<attr name="fillColor" format="color" />
<!-- Color of the filled circles that represents pages. -->
<attr name="pageColor" format="color" />
<!-- Orientation of the indicator. -->
<attr name="orientation">
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
</attr>
<!-- Radius of the circles. This is also the spacing between circles. -->
<attr name="radius" format="dimension" />
<!-- Whether or not the selected indicator snaps to the circles. -->
<attr name="snap" format="boolean" />
<!-- Color of the open circles. -->
<attr name="strokeColor" format="color" />
<!-- Width of the stroke used to draw the circles. -->
<attr name="strokeWidth" format="dimension" />
</declare-styleable>
</resources>
Now add this code to design file.
Here you have to notice that MyApplication.Droid.Library is my name space and CirclePageIndicator is my page indicator file name so replace it with your's
<MyApplication.Droid.Library.CirclePageIndicator
android:id="#+id/indicator"
android:padding="10dip"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
/>
now add the following in the activity file.
using Android.Support.V4.App;
using MyApplication.Droid.Library;
//global
public ViewPager mPager;
public PageIndicator mIndicator;
//in on create
var indicator = FindViewById<CirclePageIndicator>(Resource.Id.indicator);
mIndicator = indicator;
indicator.SetViewPager(mPager);
indicator.SetSnap(true);
I had solved this from the github
Look at this the sample project 'Android-ViewPagerIndicator to Xamarin.Android'. You need to include the library, where there are multiple implementation to display the indicators on viewpager.
https://github.com/Cheesebaron/ViewPagerIndicator
I got it working just like iOS PageControl.
Create the custom class as such:
using Android.Content;
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.Runtime;
using Android.Util;
using Android.Widget;
using System;
using System.Collections.Generic;
namespace myProject.CustomWidgets
{
public class PageControl : LinearLayout
{
#region Properties
private int _currentPage = 0;
public int CurrentPage
{
get
{
return _currentPage;
}
set
{
_currentPage = value;
SetCurrentPageIndicator();
}
}
public int Pages = 0;
public Color PageIndicatorTintColor = ColorHelper.Clear;
public Color CurrentPageIndicatorTintColor = ColorHelper.Clear;
private readonly Context context;
private List<ImageView> ivList = new List<ImageView>();
private List<Drawable> drawableList = new List<Drawable>();
private readonly int circleSize = 7;
#endregion Properties
#region Constructor
protected PageControl(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
}
public PageControl(Context context) : base(context)
{
this.context = context;
InitConfig();
}
public PageControl(Context context, IAttributeSet attrs) : base(context, attrs)
{
this.context = context;
InitConfig();
}
public PageControl(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
{
this.context = context;
InitConfig();
}
public PageControl(Context context, IAttributeSet attrs, int defStyleAttr, int defStyleRes) : base(context, attrs, defStyleAttr, defStyleRes)
{
this.context = context;
InitConfig();
}
#endregion Constructor
#region Methods
private void InitConfig()
{
Orientation = Orientation.Horizontal;
}
protected override void OnAttachedToWindow()
{
base.OnAttachedToWindow();
Render();
}
private void Render()
{
// Start with a clean slate
RemoveAllViews();
ivList = new List<ImageView>();
drawableList = new List<Drawable>();
if (Pages <= 0)
{
return;
}
for (int i = 0; i < Pages; i++)
{
var iv = new ImageView(context);
var size = ConvertionHelper.DensityToPixels(context, circleSize);
var margin = (int)(size / 2.5);
var lp = new LayoutParams(size, size);
lp.SetMargins(margin, 0, margin, 0);
iv.LayoutParameters = lp;
/* By default, all drawables instances loaded from the same resource share a common state.
* If you modify the state of one instance, all the other instances will receive the same modification.
* Calling this method on a mutable Drawable will have no effect */
var drawable = ResourcesHelper.GetDrawable(context, Resource.Drawable.ic_circle_separator).Mutate();
drawable.SetColorFilter(PageIndicatorTintColor, PorterDuff.Mode.SrcAtop);
iv.SetImageDrawable(drawable);
drawableList.Add(drawable);
ivList.Add(iv);
AddView(iv);
}
// Initial current page indicator set-up
SetCurrentPageIndicator();
}
private void SetCurrentPageIndicator()
{
if (ivList.Count == 0 || drawableList.Count == 0 || ivList.Count != drawableList.Count ||
CurrentPage < 0 || CurrentPage >= ivList.Count)
{
return;
}
// Reset all colors
for (int i = 0; i < ivList.Count; i++)
{
drawableList[i].SetColorFilter(PageIndicatorTintColor, PorterDuff.Mode.SrcIn);
ivList[i].SetImageDrawable(drawableList[i]);
}
// Change color of current page indicator
drawableList[CurrentPage].SetColorFilter(CurrentPageIndicatorTintColor, PorterDuff.Mode.SrcIn);
ivList[CurrentPage].SetImageDrawable(drawableList[CurrentPage]);
}
#endregion Methods
}
}
Then use on your xml file such as:
<myProject.PageControl
android:id="#+id/pagecontrol"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
Then on your activity you would have:
var pagecontrol = FindViewById<PageControl>(Resource.Id.pagecontrol);
pagecontrol.PageIndicatorTintColor = ColorHelper.PositiveBlue;
pagecontrol.CurrentPageIndicatorTintColor = ColorHelper.Orange;
pagecontrol.CurrentPage = 0;
pagecontrol.Pages = 3;

Vertical Scroll TextField in Blackberry

I am new to Blackberry developement.I want do the custom textfield in my application.
I write a code for that But I want to scroll the text in that text field (if me text in textfield extends above 4 line.)
I use manager class to add vertical scroll but cant get it.
TextField textfield1=new TextField(Manager.VERTICAL_SCROLL);
Anybody know how to give vertical scroll to the text field.
Try the following code:
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.component.TextField;
import net.rim.device.api.ui.container.VerticalFieldManager;
public class MyTextField extends VerticalFieldManager {
private int fieldWidth;
private int fieldHeight;
private TextField textField;
public MyTextField(int width, int height) {
super(Manager.NO_VERTICAL_SCROLL);
fieldWidth = width;
fieldHeight = height;
VerticalFieldManager vfm = new VerticalFieldManager(Manager.VERTICAL_SCROLL|Manager.VERTICAL_SCROLLBAR);
textField = new TextField() {
public void paint(Graphics g) {
getManager().invalidate();
super.paint(g);
}
};
vfm.add(textField);
add(vfm);
}
public void paint(Graphics g) {
// draw the border of the text area;
int color = g.getColor();
g.setColor(0x00FFCC);
g.drawRect(0, 0, fieldWidth, fieldHeight);
g.setColor(color);
super.paint(g);
}
public void sublayout(int width, int height) {
if (fieldWidth == 0) {
fieldWidth = width;
}
if (fieldHeight == 0) {
fieldHeight = height;
}
super.sublayout(fieldWidth, fieldHeight);
setExtent(fieldWidth,fieldHeight);
}
public String getText() {
return textField.getText();
}
public void setText(String text) {
textField.setText(text);
}
}
add it to your Screen or Manager by specifying the size like this:
add(new MyTextField(200, 200));
Create
HorizontalFieldManager HorizontalFieldManager=new HorizontalFieldManager(Manager.VERTICAL_SCROLL);
Then
TextField textfield1=new TextField();
HorizontalFieldManager.add(textfield1);
add(HorizontalFieldManager);
you can use custom textbox as bellow.
public class CustomTextBox extends Manager
{
/**
* Default margins
*/
private final static int DEFAULT_LEFT_MARGIN = 10;
private final static int DEFAULT_RIGHT_MARGIN = 10;
private final static int DEFAULT_TOP_MARGIN = 5;
private final static int DEFAULT_BOTTOM_MARGIN = 5;
/**
* Default paddings
*/
private final static int DEFAULT_LEFT_PADDING = 10;
private final static int DEFAULT_RIGHT_PADDING = 10;
private final static int DEFAULT_TOP_PADDING = 5;
private final static int DEFAULT_BOTTOM_PADDING = 5;
/**
* Margins around the text box
*/
private int topMargin = DEFAULT_TOP_MARGIN;
private int bottomMargin = DEFAULT_BOTTOM_MARGIN;
private int leftMargin = DEFAULT_LEFT_MARGIN;
private int rightMargin = DEFAULT_RIGHT_MARGIN;
/**
* Padding around the text box
*/
private int topPadding = DEFAULT_TOP_PADDING;
private int bottomPadding = DEFAULT_BOTTOM_PADDING;
private int leftPadding = DEFAULT_LEFT_PADDING;
private int rightPadding = DEFAULT_RIGHT_PADDING;
/**
* Amount of empty space horizontally around the text box
*/
private int totalHorizontalEmptySpace = leftMargin + leftPadding
+ rightPadding + rightMargin;
/**
* Amount of empty space vertically around the text box
*/
private int totalVerticalEmptySpace = topMargin + topPadding
+ bottomPadding + bottomMargin;
/**
* Minimum height of the text box required to display the text entered
*/
private int minHeight = getFont().getHeight() + totalVerticalEmptySpace;
/**
* Width of the text box
*/
private int width = Display.getWidth();
/**
* Height of the text box
*/
private int height = minHeight;
/**
* Background image for the text box
*/
private EncodedImage backgroundImage;
/**
* Bitmap version of the backgroundImage.
* Needed to reduce the calculation overhead incurred by
* scaling of the given image
* and derivation of Bitmap from the
* EncodedImage every time it is needed.
*/
private Bitmap bitmapBackgroundImage;
/**
* The core element of this text box
*/
private EditField editField;
//private BasicEditField editField;
//private String entireText;
public CustomTextBox()
{
// Let the super class initialize the core
super(0);
// An edit field is the sole field of this manager.
editField = new EditField();
//editField = new CustomEditField();
add(editField);
}
public CustomTextBox(EncodedImage backgroundImage)
{
this();
setBackgroundImage(backgroundImage);
}
public void setSize(int width, int height)
{
boolean isChanged = false;
if (width > 0 // Ignore invalid width
&& this.width != width)
{
this.width = width;
isChanged = true;
}
// Ignore the specified height if it is less
// than the minimum height required to display the text.
if (height > minHeight && height != this.height)
{
this.height = height;
isChanged = true;
}
// If width/height has been changed and background image
// is available, adapt it to the new dimension
if (isChanged == true && backgroundImage != null)
{
bitmapBackgroundImage = getScaledBitmapImage(backgroundImage,
this.width - (leftMargin+rightMargin),
this.height - (topMargin+bottomMargin));
}
}
public void setWidth(int width)
{
if (width > 0 && width != this.width)
{
this.width = width;
// If background image is available, adapt it to the new width
if (backgroundImage != null)
{
bitmapBackgroundImage = getScaledBitmapImage(backgroundImage,
this.width - (leftMargin+rightMargin),
this.height - (topMargin+bottomMargin));
}
}
}
public void setHeight(int height)
{
// Ignore the specified height if it is
// less than the minimum height required to display the text.
if (height > minHeight)
{
this.height = height;
// If background image is available, adapt it to the new width
if (backgroundImage != null)
{
bitmapBackgroundImage = getScaledBitmapImage(backgroundImage,
this.width - (leftMargin+rightMargin),
this.height - (topMargin+bottomMargin));
}
}
}
public void setBackgroundImage(EncodedImage backgroundImage)
{
this.backgroundImage = backgroundImage;
// Consider the height of background image in
// calculating the height of the text box.
// setHeight() does not ensure that specified
// height will be in effect, of course, for valid reasons.
// So derivation of Bitmap image in the setHeight() method is not sure.
setHeight(backgroundImage.getHeight() + topMargin + bottomMargin);
if (bitmapBackgroundImage == null)
{
bitmapBackgroundImage = getScaledBitmapImage(backgroundImage,
this.width - (leftMargin+rightMargin),
this.height - (topMargin+bottomMargin));
}
}
/**
* Generate and return a Bitmap Image scaled according
* to the specified width and height.
*
* #param image EncodedImage object
* #param width Intended width of the returned Bitmap object
* #param height Intended height of the returned Bitmap object
* #return Bitmap object
*/
private Bitmap getScaledBitmapImage(EncodedImage image, int width, int height)
{
// Handle null image
if (image == null)
{
return null;
}
int currentWidthFixed32 = Fixed32.toFP(image.getWidth());
int currentHeightFixed32 = Fixed32.toFP(image.getHeight());
int requiredWidthFixed32 = Fixed32.toFP(width);
int requiredHeightFixed32 = Fixed32.toFP(height);
int scaleXFixed32 = Fixed32.div(currentWidthFixed32, requiredWidthFixed32);
int scaleYFixed32 = Fixed32.div(currentHeightFixed32, requiredHeightFixed32);
image = image.scaleImage32(scaleXFixed32, scaleYFixed32);
return image.getBitmap();
}
protected void sublayout(int width, int height)
{
// We have one and only child - the edit field.
// Place it at the appropriate place.
Field field = getField(0);
layoutChild(field, this.width - totalHorizontalEmptySpace,
this.height - totalVerticalEmptySpace);
setPositionChild(field, leftMargin+leftPadding, topMargin+topPadding);
setExtent(this.width, this.height);
}
public void setTopMargin(int topMargin)
{
this.topMargin = topMargin;
}
public void setBottomMargin(int bottomMargin)
{
this.bottomMargin = bottomMargin;
}
protected void paint(Graphics graphics)
{
// Draw background image if available, otherwise draw a rectangle
if (bitmapBackgroundImage == null)
{
graphics.drawRect(leftMargin, topMargin,
// width - (leftMargin+rightMargin),
// height - (topMargin+bottomMargin));
graphics.drawRoundRect(leftMargin, topMargin,
width - (leftMargin+rightMargin),
height - (topMargin+bottomMargin), 5, 5);
}
else
{
graphics.drawBitmap(leftMargin, topMargin,
width - (leftMargin+rightMargin),
height - (topMargin+bottomMargin),
bitmapBackgroundImage, 0, 0);
}
// Determine the rightward text that can fit into the visible edit field
EditField ef = (EditField)getField(0);
String entireText = ef.getText();
boolean longText = false;
String textToDraw = "";
Font font = getFont();
int availableWidth = width - totalHorizontalEmptySpace;
if (font.getAdvance(entireText) <= availableWidth)
{
textToDraw = entireText;
}
else
{
int endIndex = entireText.length();
for (int beginIndex = 1; beginIndex < endIndex; beginIndex++)
{
textToDraw = entireText.substring(beginIndex);
if (font.getAdvance(textToDraw) <= availableWidth)
{
longText = true;
break;
}
}
}
if (longText == true)
{
// Force the edit field display only the truncated text
ef.setText(textToDraw);
// Now let the components draw themselves
super.paint(graphics);
// Return the text field its original text
ef.setText(entireText);
}
else
{
super.paint(graphics);
}
}
public int getPreferredWidth()
{
return width;
}
public int getPreferredHeight()
{
return height;
}
protected boolean keyChar(char ch, int status, int time)
{
if (ch == Characters.ENTER)
{
return true;
}
else
{
return super.keyChar(ch, status, time);
}
}
public String getText()
{
return ((EditField)getField(0)).getText();
}
public void setText(final String text)
{
((EditField)getField(0)).setText(text);
}
}

customize Edit field on blackberry

I want to customize Edit Field like in this link http://www.hostingpics.net/viewer.php?id=44669634im.png.
I find this code
------------------------------------------------CustomTextBox---------------------
package mypackage;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.component.EditField;
import net.rim.device.api.ui.component.BasicEditField;
import net.rim.device.api.system.EncodedImage;
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.system.Display;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.system.Characters;
import net.rim.device.api.math.Fixed32;
import net.rim.device.api.ui.DrawStyle;
import net.rim.device.api.ui.Font;
public class CustomTextBox extends Manager
{
/**
* Default margins
*/
private final static int DEFAULT_LEFT_MARGIN = 10;
private final static int DEFAULT_RIGHT_MARGIN = 10;
private final static int DEFAULT_TOP_MARGIN = 5;
private final static int DEFAULT_BOTTOM_MARGIN = 5;
/**
* Default paddings
*/
private final static int DEFAULT_LEFT_PADDING = 10;
private final static int DEFAULT_RIGHT_PADDING = 10;
private final static int DEFAULT_TOP_PADDING = 5;
private final static int DEFAULT_BOTTOM_PADDING = 5;
/**
* Margins around the text box
*/
private int topMargin = DEFAULT_TOP_MARGIN;
private int bottomMargin = DEFAULT_BOTTOM_MARGIN;
private int leftMargin = DEFAULT_LEFT_MARGIN;
private int rightMargin = DEFAULT_RIGHT_MARGIN;
/**
* Padding around the text box
*/
private int topPadding = DEFAULT_TOP_PADDING;
private int bottomPadding = DEFAULT_BOTTOM_PADDING;
private int leftPadding = DEFAULT_LEFT_PADDING;
private int rightPadding = DEFAULT_RIGHT_PADDING;
/**
* Amount of empty space horizontally around the text box
*/
private int totalHorizontalEmptySpace = leftMargin + leftPadding
+ rightPadding + rightMargin;
/**
* Amount of empty space vertically around the text box
*/
private int totalVerticalEmptySpace = topMargin + topPadding
+ bottomPadding + bottomMargin;
/**
* Minimum height of the text box required to display the text entered
*/
private int minHeight = getFont().getHeight() + totalVerticalEmptySpace;
/**
* Width of the text box
*/
private int width = Display.getWidth();
/**
* Height of the text box
*/
private int height = minHeight;
/**
* Background image for the text box
*/
private EncodedImage backgroundImage;
/**
* Bitmap version of the backgroundImage.
* Needed to reduce the calculation overhead incurred by
* scaling of the given image
* and derivation of Bitmap from the
* EncodedImage every time it is needed.
*/
private Bitmap bitmapBackgroundImage;
/**
* The core element of this text box
*/
private EditField editField;
//private BasicEditField editField;
//private String entireText;
public CustomTextBox()
{
// Let the super class initialize the core
super(0);
// An edit field is the sole field of this manager.
editField = new EditField();
//editField = new CustomEditField();
add(editField);
}
public CustomTextBox(EncodedImage backgroundImage)
{
this();
setBackgroundImage(backgroundImage);
}
public void setSize(int width, int height)
{
boolean isChanged = false;
if (width > 0 // Ignore invalid width
&& this.width != width)
{
this.width = width;
isChanged = true;
}
// Ignore the specified height if it is less
// than the minimum height required to display the text.
if (height > minHeight && height != this.height)
{
this.height = height;
isChanged = true;
}
// If width/height has been changed and background image
// is available, adapt it to the new dimension
if (isChanged == true && backgroundImage != null)
{
bitmapBackgroundImage = getScaledBitmapImage(backgroundImage,
this.width - (leftMargin+rightMargin),
this.height - (topMargin+bottomMargin));
}
}
public void setWidth(int width)
{
if (width > 0 && width != this.width)
{
this.width = width;
// If background image is available, adapt it to the new width
if (backgroundImage != null)
{
bitmapBackgroundImage = getScaledBitmapImage(backgroundImage,
this.width - (leftMargin+rightMargin),
this.height - (topMargin+bottomMargin));
}
}
}
public void setHeight(int height)
{
// Ignore the specified height if it is
// less than the minimum height required to display the text.
if (height > minHeight)
{
this.height = height;
// If background image is available, adapt it to the new width
if (backgroundImage != null)
{
bitmapBackgroundImage = getScaledBitmapImage(backgroundImage,
this.width - (leftMargin+rightMargin),
this.height - (topMargin+bottomMargin));
}
}
}
public void setBackgroundImage(EncodedImage backgroundImage)
{
this.backgroundImage = backgroundImage;
// Consider the height of background image in
// calculating the height of the text box.
// setHeight() does not ensure that specified
// height will be in effect, of course, for valid reasons.
// So derivation of Bitmap image in the setHeight() method is not sure.
setHeight(backgroundImage.getHeight() + topMargin + bottomMargin);
if (bitmapBackgroundImage == null)
{
bitmapBackgroundImage = getScaledBitmapImage(backgroundImage,
this.width - (leftMargin+rightMargin),
this.height - (topMargin+bottomMargin));
}
}
/**
* Generate and return a Bitmap Image scaled according
* to the specified width and height.
*
* #param image EncodedImage object
* #param width Intended width of the returned Bitmap object
* #param height Intended height of the returned Bitmap object
* #return Bitmap object
*/
private Bitmap getScaledBitmapImage(EncodedImage image, int width, int height)
{
// Handle null image
if (image == null)
{
return null;
}
int currentWidthFixed32 = Fixed32.toFP(image.getWidth());
int currentHeightFixed32 = Fixed32.toFP(image.getHeight());
int requiredWidthFixed32 = Fixed32.toFP(width);
int requiredHeightFixed32 = Fixed32.toFP(height);
int scaleXFixed32 = Fixed32.div(currentWidthFixed32, requiredWidthFixed32);
int scaleYFixed32 = Fixed32.div(currentHeightFixed32, requiredHeightFixed32);
image = image.scaleImage32(scaleXFixed32, scaleYFixed32);
return image.getBitmap();
}
protected void sublayout(int width, int height)
{
// We have one and only child - the edit field.
// Place it at the appropriate place.
Field field = getField(0);
layoutChild(field, this.width - totalHorizontalEmptySpace,
this.height - totalVerticalEmptySpace);
setPositionChild(field, leftMargin+leftPadding, topMargin+topPadding);
setExtent(this.width, this.height);
}
public void setTopMargin(int topMargin)
{
this.topMargin = topMargin;
}
public void setBottomMargin(int bottomMargin)
{
this.bottomMargin = bottomMargin;
}
protected void paint(Graphics graphics)
{
// Draw background image if available, otherwise draw a rectangle
if (bitmapBackgroundImage == null)
{
graphics.drawRect(leftMargin, topMargin,
width - (leftMargin+rightMargin), height - (topMargin+bottomMargin));
graphics.drawRoundRect(leftMargin, topMargin,
width - (leftMargin+rightMargin),
height - (topMargin+bottomMargin), 5, 5);
}
else
{
graphics.drawBitmap(leftMargin, topMargin,
width - (leftMargin+rightMargin),
height - (topMargin+bottomMargin),
bitmapBackgroundImage, 0, 0);
}
// Determine the rightward text that can fit into the visible edit field
EditField ef = (EditField)getField(0);
String entireText = ef.getText();
boolean longText = false;
String textToDraw = "";
Font font = getFont();
int availableWidth = width - totalHorizontalEmptySpace;
if (font.getAdvance(entireText) <= availableWidth)
{
textToDraw = entireText;
}
else
{
int endIndex = entireText.length();
for (int beginIndex = 1; beginIndex < endIndex; beginIndex++)
{
textToDraw = entireText.substring(beginIndex);
if (font.getAdvance(textToDraw) <= availableWidth)
{
longText = true;
break;
}
}
}
if (longText == true)
{
// Force the edit field display only the truncated text
ef.setText(textToDraw);
// Now let the components draw themselves
super.paint(graphics);
// Return the text field its original text
ef.setText(entireText);
}
else
{
super.paint(graphics);
}
}
public int getPreferredWidth()
{
return width;
}
public int getPreferredHeight()
{
return height;
}
protected boolean keyChar(char ch, int status, int time)
{
if (ch == Characters.ENTER)
{
return true;
}
else
{
return super.keyChar(ch, status, time);
}
}
public String getText()
{
return ((EditField)getField(0)).getText();
}
public void setText(final String text)
{
((EditField)getField(0)).setText(text);
}
}
--------------------------------------------MyScreen------------------------
package mypackage;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.container.MainScreen;
/**
* A class extending the MainScreen class, which provides default standard
* behavior for BlackBerry GUI applications.
*/
public final class MyScreen extends MainScreen
{
public MyScreen()
{
new CustomTextBox();
}
}
--------------------------------------------MyApp------------------------
package mypackage;
import net.rim.device.api.ui.UiApplication;
/**
* This class extends the UiApplication class, providing a
* graphical user interface.
*/
public class MyApp extends UiApplication
{
public static void main(String[] args)
{
// Create a new instance of the application and make the currently
// running thread the application's event dispatch thread.
MyApp theApp = new MyApp();
theApp.enterEventDispatcher();
}
public MyApp()
{
// Push a screen onto the UI stack for rendering.
pushScreen(new MyScreen());
}
}
But I obtain white Screen what should I change in this code to ibtain the custon edit field
hi you just create object but you forget to add that object to mainscreen
package mypackage;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.container.MainScreen;
/**
* A class extending the MainScreen class, which provides default standard
* behavior for BlackBerry GUI applications.
*/
public final class MyScreen extends MainScreen
{
public MyScreen()
{
add(new CustomTextBox());//in your code it is like new CustomTextBox();
}
}
if you want to add any image as background to your editbox then you can yous following way
package mypackage;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.container.MainScreen;
/**
* A class extending the MainScreen class, which provides default standard
* behavior for BlackBerry GUI applications.
*/
public final class MyScreen extends MainScreen
{
public MyScreen()
{
EncodedImage enc_img=EncodedImage.getEncodedImageResource("input-box.png");//image name is 'input-box.png'
CustomTextBox edi_box=new CustomTextBox(enc_img);
add(edi_box);
}
}

Resources