Android: установить фон вложенного представления в пользовательском представлении

#java #android #overriding #android-custom-view #custom-view

#java #Android #переопределение #android-пользовательский вид #пользовательский вид

Вопрос:

Итак, у меня есть эта проблема в моем пользовательском представлении. Я пытаюсь создать пользовательский RelativeLayout интерфейс с бесконечной анимацией прокрутки. Для достижения этой цели я создал макет backgroundscrollrelativelayout.xml следующим образом:

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">

<ImageView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@ id/mainTile"/>

<ImageView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@ id/topTile" />

<ImageView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@ id/leftTile" />

<ImageView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@ id/diagonalTile" />

</RelativeLayout>
  

Идея в том, что ImageView s переведут свою позицию при обратном вызове обновления анимации.

Я создал BackgroundScrollRelativeLayout.java подобное так:

 public class BackgroundScrollRelativeLayout extends RelativeLayout {
final int layoutToUse = R.layout.backgroundscrollrelativelayout;
final int mainTileId = R.id.mainTile;
final int leftTileId = R.id.leftTile;
final int topTileId = R.id.topTile;
final int diagonalTileId = R.id.diagonalTile;

final float valueStart = 0.0f;
final float valueEnd = 1.0f;
final long animationDuration = 50000L;

private Context mContext;
private ValueAnimator scrollAnimator;

private ImageView mainTile;
private ImageView leftTile;
private ImageView topTile;
private ImageView diagonalTile;


public BackgroundScrollRelativeLayout(Context context) {
    super(context);
    mContext = context;
    acquireViewsInLayout();
    initializeAnimator();
    scrollAnimator.start();
}

public BackgroundScrollRelativeLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    mContext = context;
    acquireViewsInLayout();
    initializeAnimator();
    scrollAnimator.start();
}


public BackgroundScrollRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    mContext = context;
    acquireViewsInLayout();
    initializeAnimator();
    scrollAnimator.start();
}

@Override
public void setBackgroundColor(int color) {
    // Not supported
}

@Override
public void setBackgroundResource(int resid) {
    // Not supported
}


@Override
public void setBackground(Drawable background) {
    mainTile.setBackground(background);
    leftTile.setBackground(background);
}

/*
@Override
public void setBackgroundDrawable(Drawable background) {
    //super.setBackgroundDrawable(background);
    //mainTile.setBackground(background);
    //leftTile.setBackground(background);
}*/

// Intent: Inflate the layout associated with this View
private void inflateLayout(){
    // TO inflateLayout, we connect the inflater to context, then we call inflate the layout associated
    // with this view

    //LayoutInflater inflater = LayoutInflater.from(mContext);
    //inflater.inflate(layoutToUse, this);
    inflate(getContext(), layoutToUse, this);
}

// Intent: Find all Views in Layout
private void findAllViewsById(){
    mainTile = (ImageView)this.findViewById(mainTileId);
    leftTile = (ImageView)this.findViewById(leftTileId);
    topTile = (ImageView)this.findViewById(topTileId);
    diagonalTile = (ImageView)this.findViewById(diagonalTileId);
}

// Intent: Concretely acquire all Views in Layout
private void acquireViewsInLayout(){
    // TO acquireViewsInLayout, we inflate the layout,
    // then we find the view of each known view id and save the view
    inflateLayout();
    findAllViewsById();
}

// Intent: Initialize animator properties
private void initializeAnimator(){
    // TO initializeAnimator, we set how the animator will keep track of animation,
    // then we set the animation repeat type, then we set the type of interpolation,
    // then we set the animation duration, then we apply animation update listener
    scrollAnimator = ValueAnimator.ofFloat(valueStart, valueEnd);
    scrollAnimator.setRepeatCount(ValueAnimator.INFINITE);
    scrollAnimator.setInterpolator(new LinearInterpolator());
    scrollAnimator.setDuration(animationDuration);
    addScrollAnimatorUpdateListener();
}

// Intent: Add an update listener to the scroll animator
private void addScrollAnimatorUpdateListener(){
    // TO addScrollAnimatorUpdateListener, we add an update listener to scroll animator
    scrollAnimator.addUpdateListener( new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            // Do something...
            updateScrollAnimation();
        }
    });

}

private void updateScrollAnimation(){
    float progress = (float)scrollAnimator.getAnimatedValue();
    float widthOfTile = mainTile.getWidth();

    float moveInXAxis = widthOfTile * progress;

    mainTile.setTranslationX(moveInXAxis);
    leftTile.setTranslationX(moveInXAxis - widthOfTile);


    // Ignore the rest for now
    topTile.setTranslationY(-1.0f);
    diagonalTile.setTranslationX(-1.0f);
    diagonalTile.setTranslationY(-1.0f);

}



}
  

Я использую свой пользовательский вид таким образом в действии:

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.martianstudio.adivinaque.AppSettingsActivity">

<com.martianstudio.adivinaque.BackgroundScrollRelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/main_activity_animation_icon" />
</RelativeLayout>
  

Цель этого — позволить пользователю указать фон, с которым они хотят прокручивать android:background . Я не хочу, чтобы родитель RelativeLayout использовал этот чертеж в качестве фона. Вместо этого я хочу ImageViews , чтобы в качестве фона был установлен объект рисования. Я переопределил setBackground в своем пользовательском представлении, чтобы только ImageView s устанавливали в качестве фона изображение, а не RelativeLayout (как это было бы по умолчанию).

 @Override
public void setBackground(Drawable background) {
    mainTile.setBackground(background);
    leftTile.setBackground(background);
}
  

Однако я получаю эту ошибку:

 java.lang.NullPointerException at com.martianstudio.adivinaque.BackgroundScrollRelativeLayout.setBackground
  

Это говорит о том, что mainTile in setBackground не был установлен (он равен нулю). В моем findAllViewsById() я явно пишу this.mainTile = (ImageView)this.findViewById(mainTileId); , где mainTileId = R.id.mainTile , и я раздуваю макет inflateLayout() . Оба этих метода вызываются в конструкторах класса. Мне кажется, что по какой-то причине он не может найти ImageView с mainTile идентификатором, когда я переопределяю setBackground метод, как я это делаю. Если я не переопределю setBackground метод, я не получу исключение NullPointerException, однако по умолчанию используется метод setBackground по умолчанию, чего я не хочу. Я что-то упускаю?

Любая помощь будет оценена по достоинству :). Спасибо!

Комментарии:

1. Может быть, это работает так: this в inflateLayout() — это rooView, содержащий CustomView. rootView не содержит mainTile ImageView, leftTile .. и т.д. Так что, если вы используете this , чтобы findViewById вернуться null .

2. @HoangNguyen У меня есть mainTile.setTranslationX(moveInAxis) позже в функции updateScrollAnimation, и он не жалуется на нулевой указатель. Я думаю, что когда setBackground() вызывается системой Android, он делает это до того, как раздувает макет, чтобы findViewById возвращал значение null. Я думаю, мне нужно будет получить извлекаемый ресурс через attrs. Я проверю, если что-нибудь найду. Спасибо за комментарий!