viernes, 25 de julio de 2014

Android: Smooth scrolling tricks (ListView)


@Override
 public View getGroupView(int groupPosition, boolean isExpanded,
   View convertView, ViewGroup parent) {

  ViewHolder holder;

  if (convertView == null) {
   
   convertView = mInflater.inflate(R.layout.row, null);

   holder = createViewHolder(convertView);
   
   convertView.setTag(holder);

  } else {

   holder = (ViewHolder) convertView.getTag();
  }
  
  IModel model = getGroup(groupPosition);
  
  holder.text1.setText(model.getText1());
  holder.layout.setBackgroundColor(context.getBackgroundColor(model));
  
  return convertView;
 }


private ViewHolder createViewHolder(View convertView) {

  ViewHolder holder;
  holder = new ViewHolder();

  holder.text1 = (TextView) convertView.findViewById(R.id.row_text1);
  holder.layout = (LinearLayout) convertView.findViewById(R.id.layoutRow);

  return holder;
 }

Android: ImageView wrap resized image.


getWomanPictureView().getViewTreeObserver().addOnGlobalLayoutListener(
    new OnGlobalLayoutListener() {

     @Override
     public void onGlobalLayout() {

      Bitmap image = getFragment().getWomanPictureBitmap(
        getWomanPictureView(), "fotito_normal");

      getWomanPictureView().setImageBitmap(image);

      removeOnGlobalLayoutListener(getWomanPictureView());
     }

     @SuppressWarnings("deprecation")
     @SuppressLint("NewApi")
     private void removeOnGlobalLayoutListener(
       final ImageView image) {

      int currentAPIVersion = android.os.Build.VERSION.SDK_INT;

      if (currentAPIVersion >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
       image.getViewTreeObserver()
         .removeOnGlobalLayoutListener(this);
      } else {
       image.getViewTreeObserver()
         .removeGlobalOnLayoutListener(this);
      }
     }
    });

public Bitmap getWomanPictureBitmap(ImageView womanPictureView,
   String womanPicture) {

  int womanPictureResID = getActivity().getResources().getIdentifier(
    womanPicture, "drawable", getActivity().getPackageName());

  Bitmap image = BitmapFactory.decodeResource(getResources(),
    womanPictureResID);

  int width = womanPictureView.getWidth();

  if (width != 0 && width != image.getWidth()) {

   int height = width * image.getHeight() / image.getWidth();

   Bitmap scaledImage = Bitmap.createScaledBitmap(image, width,
     height, false);

   image.recycle();

   return scaledImage;

  } else {

   return image;
  }
 }

martes, 22 de julio de 2014

Android: Custom action bar

Using sherlock:

Manifest
<resources xmlns:android="http://schemas.android.com/apk/res/android">

    <style name="AppTheme" parent="Theme.Sherlock.Light.DarkActionBar">
</resources>

Activity
public class AbstractActivity extends SherlockActivity {
...
public BaseActivity(boolean hasMenuItemInActionBar) {
 super();
  
 this.hasMenuItemInActionBar = hasMenuItemInActionBar;
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

 if (this.getSupportActionBar() != null) {
  this.getSupportActionBar().setDisplayHomeAsUpEnabled(false);
  this.getSupportActionBar().setDisplayShowHomeEnabled(false);
  this.getSupportActionBar().setDisplayUseLogoEnabled(false);
  this.getSupportActionBar().setDisplayShowCustomEnabled(true);
  this.getSupportActionBar().setCustomView(R.layout.actionbar_custom);
   
  setTitle(this.getTitle());
 }
}
...
}

Using appcompat


Manifest
<resources xmlns:android="http://schemas.android.com/apk/res/android">

    <style name="AppTheme" parent="@style/Theme.AppCompat.Light">
</resources>

Activity
public class AbstractActivity extends ActionBarActivity{
...
public BaseActivity(boolean hasMenuItemInActionBar) {
 super();
  
 this.hasMenuItemInActionBar = hasMenuItemInActionBar;
}

@Override
protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

 if (this.getSupportActionBar() != null) {
  this.getSupportActionBar().setDisplayHomeAsUpEnabled(false);
  this.getSupportActionBar().setDisplayShowHomeEnabled(false);
  this.getSupportActionBar().setDisplayUseLogoEnabled(false);
  this.getSupportActionBar().setDisplayShowCustomEnabled(true);
  this.getSupportActionBar().setCustomView(R.layout.actionbar_custom);
   
  setTitle(this.getTitle());
 }
}
...
}

Android: Spinner with hint text and "setError" like functionality


Adapter:

public class GenderAdapter extends BaseAdapter {

    private Context context;
    private String[] genderList;

    public GenderAdapter(Context context) {
        this.context = context;
        this.genderList = new String[3];
        this.genderList[0] = context.getString(R.string.gender); // Hint text
        this.genderList[1] = context.getString(R.string.male);
        this.genderList[2] = context.getString(R.string.female);
    }

    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent) {

        if (convertView == null) {
            convertView = LayoutInflater.from(context).inflate(R.layout.drop_down_row_light_gray, null);
        }

        TextView text = (TextView) convertView.findViewById(R.id.text);
        text.setText(getItem(position));

        Resources resources = context.getResources();
        text.setTextColor(resources.getColor(position == 0 ? R.color.gray : R.color.dark_gray)); // If it is the hint text (position == 0) set the textcolor to be a lighter color


        return convertView;
    }

    @Override
    public int getCount() {
        return genderList.length;
    }

    @Override
    public String getItem(int position) {
        return genderList[position];
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        if (convertView == null) {
            convertView = LayoutInflater.from(context).inflate(R.layout.closed_row_light_gray, null);
        }

        TextView text = (TextView) convertView.findViewById(R.id.text);
        text.setText(getItem(position));

        Resources resources = context.getResources();
        text.setTextColor(resources.getColor(position == 0 ? R.color.gray : R.color.dark_gray)); // If it is the hint text (position == 0) set the textcolor to be a lighter color

        return convertView;
    }
}
drop_down_row_light_gray:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/text"
    style="@style/Base.TextAppearance.AppCompat.Medium"
    android:background="@color/light_gray"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:minHeight="48dp"
    android:paddingLeft="8dp"
    android:paddingRight="16dp"
    android:paddingTop="16dp"
    android:paddingBottom="16dp"
    android:ellipsize="end"
    android:textColor="@color/dark_gray"
    android:gravity="center"/>
closed_row_light_gray:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/text"
    style="@style/Base.TextAppearance.AppCompat.Medium"
    android:background="@android:color/transparent"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:minHeight="48dp"
    android:paddingLeft="8dp"
    android:paddingRight="16dp"
    android:paddingTop="16dp"
    android:paddingBottom="16dp"
    android:textColor="@color/dark_gray"
    android:gravity="center"/>
Add "setError" to the spinner :
private void setSubmitButtonOnClickListener() {

        submitButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if (isFormValid()) {
                    ...

                } else {
 

                    if (isAgeEmpty()) {
                        TextView textView = (TextView) ageSpinner.getSelectedView().findViewById(R.id.text); // "text" is the layout id of the TextView that displays the text
                        textView.setError(getString(R.string.age_is_required));
                    }

                    ...
                }
            }
        });
    }

Notes:
1) In my experience, when I used 9 patch as the spinne's background, the spinner's text dissapeared. It does not happen if I use a "normal" drawable or background color.
2) The "arrow thingy" to the right side of a spinner is actually a background (a 9 patch png). That being said, if you change the spinne background the arrow will dissapear.ng>

Android: White screen when coming back from activity B to activiy A

In the theme of the application or activity's theme add:

<style name="AppTheme" parent="@style/Theme.AppCompat">
       
        // ...
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowFullscreen">true</item>
        <item name="android:background">@android:color/transparent</item>
        <item name="android:windowBackground">@android:color/transparent</item>
    </style>

jueves, 17 de julio de 2014

Android: Easy share


Uri uri = Uri.fromFile(new File(path));
       
Intent shareIntent = new Intent(Intent.ACTION_SEND);  
shareIntent.setType("image/*");  
shareIntent.putExtra(Intent.EXTRA_TEXT, "text"); 
shareIntent.putExtra(Intent.EXTRA_TITLE, "title"); 
shareIntent.putExtra(Intent.EXTRA_SUBJECT, "subject");
shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
shareIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); *
startActivity(Intent.createChooser(shareIntent, "Share via"));

Note: This can be used with facebook if the application is installed.


* If you leave out the flag, when returning to your app (from homescreen, from recents etc.), you would see the Activity of the share target (messaging/mailing/IM app) instead of yours.







Android: Update ADT to 23

1) Go to Help --> About Eclipse SDK --> Installation Details

2) Select from the list the following plugins:

- Android DDMS
- Android Development Tools
- Android Hierarchy Viewer
- Android Native Development Tools
- Android Traceview
- Tracer for OpenGL ES

Click uninstall and follow the steps

4) Go to Help > Install New Software > http://dl-ssl.google.com/android/eclipse/
5) Select all plugins, and follow the steps and you are done.

viernes, 4 de julio de 2014

Android: Hide keyboard

1) Hide keyboard

private void hideKeyboard() {

EditText calibationEditText = (EditText) findViewById(R.id.calibration);

InputMethodManager imm = (InputMethodManager) 
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(calibationEditText.getWindowToken(), 0);

calibationEditText.clearFocus();
}

2) I created a dialog with an EditText when I close the dialog the keyboard doesn't disappear, it only changed from input type number to normal.

AndroidManifest.xml

<activity
            android:name="ar.com.fennoma.digipad.activities.Activity"
            android:label="@string/app_name"
            android:windowSoftInputMode="stateAlwaysHidden" >
</activity>

Activity is the activity that creates the dialog.

Android: JNI

Compiling

To be able to compile JNI code you need to create a make file. You can fin examples of working make files in the samples here. I named the Vuforia Android samples since it was because of this library that I had to work with JNI :P

In windows you just have to type the following the commands promt:

cd <path_to_jni_files>
<path_to_android_ndk>\ndk-build


For example:

cd C:\Users\dev\Desktop\Repositories\MyProject\jni
C:\android-ndk-r9c\ndk-build


You can find the Android NDK here. You can find the oficial documentation about JNI here.

Loading libs

The compiled library needs to be placed inside "/libs" inside, most likely, the folder named "armeabi-v7a".

For example, to load the library named "libdetection_based_tracker.so" somewhere in your code you have to call the following method: "System.loadLibrary("detection_based_tracker");".

Create an object

Example: Instantiate a Float object

jclass floatClass = env->FindClass("java/lang/Float"); // 1

jmethodID floatConstructor = env->GetMethodID(floatClass, "<init>", "(F)V"); // 2

jobject float object = env->NewObject(floatClass, floatConstructor, 5.0); // 3

1) We first need to find the class will be working on, in this case, the float class. env is the environment pointer is of type (JNIEnv).
2) Get the constructor method.
<init> indicates its a constructor.
(F)V defines the constructor arguments (the characters inside the parentheses) and the return type (just after the parentheses). In this case the arguments is just a single float (F) and the return type is void (V). You can check all type signatures here.
3) Create a java object given the class (floatClass), the constructor (floatConstructor) and the arguments of the constructor is 5.0.

Calling methods from Android methods with JNI

jclass activityClass = env->GetObjectClass(obj);

jmethodID targetDetectedMethodID = env->GetMethodID(activityClass, "targetDetected", "([Ljava/lang/Object;)V");

if (targetDetectedMethodID == 0) {

   LOG("Function targetDetected() not found.");

   return;
}

env->CallVoidMethod(obj, targetDetectedMethodID, arguments); //1


1) Arguments can be any objects or a var_list

miércoles, 2 de julio de 2014

Android: Obtain image size from file

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(resources, resourceId, options);

// options.outHeight
// options.outWidth

Android: Multiple option share dialog

Intent shareIntent = new Intent(Intent.ACTION_SEND);
        shareIntent.setType("text/plain");
        shareIntent.putExtra(Intent.EXTRA_TEXT, "text");
        shareIntent.putExtra(Intent.EXTRA_TITLE, "title");
        shareIntent.putExtra(Intent.EXTRA_SUBJECT, "subject");
        startActivity(Intent.createChooser(shareIntent, "Share via"));