I want get text string shown in a textview in LinearLayout. can espresso do that? If not, is there other way to do that or can I use android api in espresso test case? I am using API 17 18 or newer, espresso 1.1(It should be the latest one.). I have no clue about this. Thanks.
The basic idea is to use a method with an internal ViewAction that retrieves the text in its perform method. Anonymous classes can only access final fields, so we cannot just let it set a local variable of getText(), but instead an array of String is used to get the string out of the ViewAction.
String getText(final Matcher<View> matcher) {
final String[] stringHolder = { null };
onView(matcher).perform(new ViewAction() {
#Override
public Matcher<View> getConstraints() {
return isAssignableFrom(TextView.class);
}
#Override
public String getDescription() {
return "getting text from a TextView";
}
#Override
public void perform(UiController uiController, View view) {
TextView tv = (TextView)view; //Save, because of check in getConstraints()
stringHolder[0] = tv.getText().toString();
}
});
return stringHolder[0];
}
Note: This kind of view data retrievers should be used with care. If you are constantly finding yourself writing this kind of methods, there is a good chance, you're doing something wrong from the get go. Also don't ever access the View outside of a ViewAssertion or ViewAction, because only there it is made sure, that interaction is safe, as it is run from UI thread, and before execution it is checked, that no other interactions meddle.
If you want to check text value with another text, you can create Matcher. You can see my code to create your own method:
public static Matcher<View> checkConversion(final float value){
return new TypeSafeMatcher<View>() {
#Override
protected boolean matchesSafely(View item) {
if(!(item instanceof TextView)) return false;
float convertedValue = Float.valueOf(((TextView) item).getText().toString());
float delta = Math.abs(convertedValue - value);
return delta < 0.005f;
}
#Override
public void describeTo(Description description) {
description.appendText("Value expected is wrong");
}
};
}
Related
In app I'm bulding I used data model presented by James_D here:
Applying MVC With JavaFx
I just can find a way to bind labels text to property of object held in DataModel
Data is structured like this:
model class Student
//partial class
public class Student {
private final StringProperty displayName = new SimpleStringProperty();
public final StringProperty displayNameProperty(){
return this.displayName;
}
public Student(){
}
public final String getDisplayName() {
return this.displayNameProperty().get();
}
public final void setDisplayName(String displayName) {
this.displayNameProperty().set(displayName);
}
}
Student instaces are held by StudentDataModel class
public class StudentDataModel {
// complete student list
private final ObservableList<Student> studentList = FXCollections.observableArrayList();
private final ObjectProperty<Student> selectedStudent = new SimpleObjectProperty<>(new Student());
public final Student getSelectedStudent() {
return selectedStudent.get();
}
public final ObjectProperty<Student> selectedStudentProperty() {
return selectedStudent;
}
public final void setSelectedStudent(Student student) {
selectedStudent.set(student);
}
}
StudentList is displayed by Table View, there is change listener that sets selectedStudent like this:
public class TableViewController {
public void initModel(StudentDataModel studentDM) {
// ensure model is set once
if (this.studentDM != null) {
throw new IllegalStateException("StudentDataModel can only be initialized once");
}
this.studentDM = studentDM;
tableView.getSelectionModel().selectedItemProperty().addListener((obs, oldSelection, newSelection) -> {
if (newSelection != null) {
studentDM.setSelectedStudent(newSelection);
}
});
}}
There is another controller ActionsBarController that has label to display selected student (this seems redundant, but there is option for selecting multiple objects to perform bulk operations).
StudentDataModel is initialized properly (I can see it in debuger) but below doesn't do anything:
chosenStudentLabel.textProperty().bind(studentDM.getSelectedStudent().displayNameProperty());
//this shows class name with instance number changing correctly
chosenStudentLabel.textProperty().bind(studentDM.selectedStudentProperty().asString());
I could inject ActionsBarController to TableViewController and change label text from change Listener there, but this seems counter productive with data model.
What am I doing wrong?
Your code doesn't work, because you call (and evaluate) getSelectedStudent() at the time the binding is created (i.e. when you initialize the model). As a consequence, you only bind to the displayName property of the student that is selected at that time. (If nothing is selected, you'll get a NullPointerException.) The binding will only change if that initially-selected student's display name changes; it won't change if the selection changes.
You need a binding that unbinds from the old selected student's display name, and binds to the new selected student's display name, when the selected student changes. One way to do this is:
chosenStudentLabel.textProperty().bind(new StringBinding() {
{
studentDM.selectedStudentProperty().addListener((obs, oldStudent, newStudent) -> {
if (oldStudent != null) {
unbind(oldStudent.displayNameProperty());
}
if (newStudent != null) {
bind(newStudent.displayNameProperty());
}
invalidate();
});
}
#Override
protected String computeValue() {
if (studentDM.getSelectedStudent() == null) {
return "" ;
}
return studentDM.getSelectedStudent().getDisplayName();
}
});
Note that there is also a "built-in" way to do this, but it's a bit unsatisfactory (in my opinion) for a couple of reasons. Firstly, it relies on specifying the name of the "nested property" as a String, using reflection to access it. This is undesirable because it has no way to check the property exists at compile time, it requires opening the module for access, and it is less good performance-wise. Secondly, it gives spurious warnings if one of the properties in the "chain" is null (e.g. in this case if the selected student is null, which is will be initially), even though this is a supported case according to the documentation. However, it is significantly less code:
chosenStudentLabel.textProperty().bind(
Bindings.selectString(studentDM.selectedStudentProperty(), "displayName")
);
This has baffled me for a while now and I cannot seem to get the grasp of it. I'm using Cell Value Factory to populate a simple one column table and it does not populate in the table.
It does and I click the rows that are populated but I do not see any values in them- in this case String values. [I just edited this to make it clearer]
I have a different project under which it works under the same kind of data model. What am I doing wrong?
Here's the code. The commented code at the end seems to work though. I've checked to see if the usual mistakes- creating a new column instance or a new tableview instance, are there. Nothing. Please help!
//Simple Data Model
Stock.java
public class Stock {
private SimpleStringProperty stockTicker;
public Stock(String stockTicker) {
this.stockTicker = new SimpleStringProperty(stockTicker);
}
public String getstockTicker() {
return stockTicker.get();
}
public void setstockTicker(String stockticker) {
stockTicker.set(stockticker);
}
}
//Controller class
MainGuiController.java
private ObservableList<Stock> data;
#FXML
private TableView<Stock> stockTableView;// = new TableView<>(data);
#FXML
private TableColumn<Stock, String> tickerCol;
private void setTickersToCol() {
try {
Statement stmt = conn.createStatement();//conn is defined and works
ResultSet rsltset = stmt.executeQuery("SELECT ticker FROM tickerlist order by ticker");
data = FXCollections.observableArrayList();
Stock stockInstance;
while (rsltset.next()) {
stockInstance = new Stock(rsltset.getString(1).toUpperCase());
data.add(stockInstance);
}
} catch (SQLException ex) {
Logger.getLogger(WriteToFile.class.getName()).log(Level.SEVERE, null, ex);
System.out.println("Connection Failed! Check output console");
}
tickerCol.setCellValueFactory(new PropertyValueFactory<Stock,String>("stockTicker"));
stockTableView.setItems(data);
}
/*THIS, ON THE OTHER HAND, WORKS*/
/*Callback<CellDataFeatures<Stock, String>, ObservableValue<String>> cellDataFeat =
new Callback<CellDataFeatures<Stock, String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(CellDataFeatures<Stock, String> p) {
return new SimpleStringProperty(p.getValue().getstockTicker());
}
};*/
Suggested solution (use a Lambda, not a PropertyValueFactory)
Instead of:
aColumn.setCellValueFactory(new PropertyValueFactory<Appointment,LocalDate>("date"));
Write:
aColumn.setCellValueFactory(cellData -> cellData.getValue().dateProperty());
For more information, see this answer:
Java: setCellValuefactory; Lambda vs. PropertyValueFactory; advantages/disadvantages
Solution using PropertyValueFactory
The lambda solution outlined above is preferred, but if you wish to use PropertyValueFactory, this alternate solution provides information on that.
How to Fix It
The case of your getter and setter methods are wrong.
getstockTicker should be getStockTicker
setstockTicker should be setStockTicker
Some Background Information
Your PropertyValueFactory remains the same with:
new PropertyValueFactory<Stock,String>("stockTicker")
The naming convention will seem more obvious when you also add a property accessor to your Stock class:
public class Stock {
private SimpleStringProperty stockTicker;
public Stock(String stockTicker) {
this.stockTicker = new SimpleStringProperty(stockTicker);
}
public String getStockTicker() {
return stockTicker.get();
}
public void setStockTicker(String stockticker) {
stockTicker.set(stockticker);
}
public StringProperty stockTickerProperty() {
return stockTicker;
}
}
The PropertyValueFactory uses reflection to find the relevant accessors (these should be public). First, it will try to use the stockTickerProperty accessor and, if that is not present fall back to getters and setters. Providing a property accessor is recommended as then you will automatically enable your table to observe the property in the underlying model, dynamically updating its data as the underlying model changes.
put the Getter and Setter method in you data class for all the elements.
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
My users upload some images to the FileStorage at Backendless.
This is the upload sequence:
Backendless.Files.Android.upload(image1_scaled, Bitmap.CompressFormat.PNG,
100, "profileImage", "images", new AsyncCallback<BackendlessFile>() {
#Override
public void handleResponse(BackendlessFile response) {
fileMapping.profile_url = response.getFileURL();
Backendless.Data.of(FileMapping.class).save(fileMapping,
new AsyncCallback<FileMapping>() {
#Override
public void handleResponse(FileMapping response) {
toast_error("Image stored");
}
#Override
public void handleFault(BackendlessFault fault) {
System.out.println("ERROR" + fault.getCode());
}
});
}
#Override
public void handleFault(BackendlessFault fault) {
}
});
And that works flawlessly. Now I need to fetch back the image with the API to display it.
So I need to make a BackendlessCollection<FileMapping> userFiles = Backendless.Data.of(FileMapping.class) call to receive the URL back from that table. And then supposedly do a httpRequest with the url to get back the byte data.
What I can't work out is what sort of .find method to use? Do I .findById() ? And if so, what ID do I use? The "path", "name" ,"table" etc?
Could anyone show an example fitting my case here, with a table storing the url's and such?
Thanks.
You'd this something like this (showing sync call for simplicity, but make sure to change it to Async on Android):
BackendlessCollection<FileMapping> fileMappings;
fileMappings = Backendless.Data.of( FileMapping.class ).find();
Iterator<FileMapping> iterator = fileMappings.getCurrentPage().iterator();
while( iterator.hasNext() )
{
FileMapping fileMapping = iterator.next();
Log.i( "MyApp", "file URL is " + fileMapping.profile_url );
}
Got a little bit confused using ConcurrentHashMap.
So, I'm storing my own class instances as key in it.
Let's call it Job.
Here it is.
public class Job {
private long id;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#Override
public boolean equals(Object o) {
if (!(o instanceof Job))
return false;
Job job = (Job)o;
return job.getId() == getId();
}
}
Here is what I'm trying to do with it:
Job jobToAdd = new Job();
jobToAdd.setId(1);
Job jobToRemove = new Job();
jobToRemove.setId(1);
ConcurrentMap<Job, Long> jobsQueue
= new ConcurrentHashMap<>();
jobsQueue.put(jobToAdd, jobToAdd.getId());
jobsQueue.containsKey(jobToRemove); //Returns FALSE
At some point I want to remove a job from this queue. I'm using different instance of the job class, but it has the same ID. I assume that remove is searching for keys based on equals, isn't it? Well, containsKey, for sure, but even containsKey returns FALSE. However,
jobsQueue.keySet().iterator().next().equals(jobToRemove);
returns TRUE.
Am I missing something here?
Any help is greatly appreciated!
Thanks!
Andrey
Pardon my stupidity... I totally forgot to override hash function.