简体   繁体   中英

Alternative to repetitive lines of code (Android/Java)

This is my first proper application and it works great but just curious to see who I can shorten my code and make it less repetitive? Here's my code and it's for a soundboard:

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

    //Tell system to use Media Volume rather than Ringer

    setVolumeControlStream(AudioManager.STREAM_MUSIC);

    // Button references

    Button button1 = (Button) findViewById(R.id.button1);
    Button button2 = (Button) findViewById(R.id.button2);
    Button button3 = (Button) findViewById(R.id.button3);
    Button button4 = (Button) findViewById(R.id.button4);
    Button button5 = (Button) findViewById(R.id.button5);
    Button button6 = (Button) findViewById(R.id.button6);

    Button button8 = (Button) findViewById(R.id.button8);
    Button button9 = (Button) findViewById(R.id.button9);
    Button button10 = (Button) findViewById(R.id.button10);
    Button button11 = (Button) findViewById(R.id.button11);
    Button button12 = (Button) findViewById(R.id.button12);
    Button button13 = (Button) findViewById(R.id.button13);


    // Button Sounds to be used by onClickListener

    final MediaPlayer buttonSound1 = MediaPlayer.create(MainActivity.this,
            R.raw.afternoondelight);
    final MediaPlayer buttonSound2 = MediaPlayer.create(MainActivity.this,
            R.raw.alrightythen);
    final MediaPlayer buttonSound3 = MediaPlayer.create(MainActivity.this,
            R.raw.ballsshowing);
    final MediaPlayer buttonSound4 = MediaPlayer.create(MainActivity.this,
            R.raw.blackmen);
    final MediaPlayer buttonSound5 = MediaPlayer.create(MainActivity.this,
            R.raw.doh);
    final MediaPlayer buttonSound6 = MediaPlayer.create(MainActivity.this,
            R.raw.fxxk);
    final MediaPlayer buttonSound8 = MediaPlayer.create(MainActivity.this,
            R.raw.mclovin);
    final MediaPlayer buttonSound9 = MediaPlayer.create(MainActivity.this,
            R.raw.pacmandeath);
    final MediaPlayer buttonSound10 = MediaPlayer.create(MainActivity.this,
            R.raw.quickwhite);
    final MediaPlayer buttonSound11 = MediaPlayer.create(MainActivity.this,
            R.raw.sexylady);
    final MediaPlayer buttonSound12 = MediaPlayer.create(MainActivity.this,
            R.raw.troll);
    final MediaPlayer buttonSound13 = MediaPlayer.create(MainActivity.this,
            R.raw.turd);

    //onClickListeners, button7 and button14 have been removed due to explicit content.

    button1.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub

            buttonSound1.start();

        }
    });
    button2.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            buttonSound2.start();

        }
    });
    button3.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub

            buttonSound3.start();

        }
    });
    button4.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            buttonSound4.start();

        }
    });
    button5.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            buttonSound5.start();

        }
    });
    button6.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            buttonSound6.start();
        }
    });
    //Where button7 was
    button8.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            buttonSound8.start();
        }
    });
    button9.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            buttonSound9.start();
        }
    });
    button10.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            buttonSound10.start();
        }
    });
    button11.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            buttonSound11.start();
        }
    });
    button12.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            buttonSound12.start();
        }
    });
    button13.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            buttonSound13.start();

        }
    });
    //Where button14 was

there are so many ways to make the code shorter. here are some of them:

  • since each of the buttons have their id set as "button#" , you can use a loop that goes over all of them by using something like:
 int resId = getResources().getIdentifier("button" + i, "id", getPackageName()); Button b=findViewById(resId); 

then, you can set the onClickListener via a function on each of them, each time getting the correct resource to play for the current button, for example using a constant array set in the beginning:

 private static final int[] SOUNDS=new int[]{R.raw.afternoondelight,R.raw.alrightythen,... }; 
  • instead of the array of the above method , you could set the tag of each of the buttons in the xml , to point to the correct sound.

  • instead of finding the views, you could (works only for buttons) set the onClick method inside the XML, so all you need to do is to check which of the buttons was pressed in the function, and there you choose what sound to make.

  • another alternative is to create a custom view that has an attribute for the sound to play, which will allow you to do everything in XMl.

  • use third party libraries like RoboGuice or Android Query .

btw, since you have so many buttons, would you mind telling us their locations and other properties ? maybe you could make some optimizations in creating them too, instead of having so many items in the xml file... maybe you could put them in a gridView or listView, and avoid creating so many of them ...

also, please consider making the mediaPlayer a single field which will be released when not needed. the reason is both for removing the loading time and for less memory to use.

You don't need an explicit click listener for every button. Let the Actitvity implement the listener and handle your clicks in onClick. Furthermore have your MediaPlayer as a field because it isn't necessary to create 14 individual references.

public class MainActivity extends Activity implements View.OnClickListener {

    MediaPlayer sound;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

        //Tell system to use Media Volume rather than Ringer

        setVolumeControlStream(AudioManager.STREAM_MUSIC);

        // Button references

        Button button1 = (Button) findViewById(R.id.button1);
        Button button2 = (Button) findViewById(R.id.button2);
        Button button3 = (Button) findViewById(R.id.button3);
        Button button4 = (Button) findViewById(R.id.button4);
        Button button5 = (Button) findViewById(R.id.button5);
        Button button6 = (Button) findViewById(R.id.button6);

        Button button8 = (Button) findViewById(R.id.button8);
        Button button9 = (Button) findViewById(R.id.button9);
        Button button10 = (Button) findViewById(R.id.button10);
        Button button11 = (Button) findViewById(R.id.button11);
        Button button12 = (Button) findViewById(R.id.button12);
        Button button13 = (Button) findViewById(R.id.button13);

        // assign the click listener to the button

        button1.setOnClickListener(this);
        button2.setOnClickListener(this);
        // etc
}

    @Override
    public void onClick(View v) {
        switch(v.getId())
        case R.id.button1:
            sound = MediaPlayer.create(MainActivity.this,
                    R.raw.afternoondelight);
            sound.start();
            break;

        case R.id.button2:
            sound = MediaPlayer.create(MainActivity.this,
                    R.raw.alrightythen);
            sound.start();
            break;

        // and so on
    }
}

This should help for a start.

For starters you should only initialize one MediaPlayer as the class's property and on click set the resource to play and actually play it. You can create a single onClickListener and by getting the button clicked choose the sound file. Hope that helps

Since you're new to programming at all, I'd wish you to learn groovy ;) Your code could be shortened in groovy like that:

def effects = ["afternoondelight", "alrightythen", "ballsshowing", "blackmen", "doh", "fxxk", "mclovin", "pacmandeath", "quickwhite", "sexylady", "troll", "turd"] 

effects.size().times {
 Button button = (Button) findViewById(R.id."button${it}");
 MediaPlayer buttonSound = MediaPlayer.create(MainActivity.this, R.raw."${effects[it]}");
 button.onClickListener = [onClick: { View v -> buttonSound.start() } as View.OnClickListener]
}

you code is well structured. But you are right, it's evil to duplicate like this. This approach is both ugly and error prone (people make more mistake when they get more bored, devs included).

So there are a couple of things you could/should do to shrink your code down :

  • use roboguice or butterknife to avoir the findViewById statements. With RoboGuice you would declare fields like

     @InjectView( R.id.button1 ) private Button button1 

Not much better, but still just the essential, nothing more.

  • a different approach, that I would use only for MediaPlayers (but could work with buttons) is putting series in a factory method.

     MediaPlayer buttonSound1 = createPlayerForResource( R.raw.afternoondelight ); 
  • and then you can use a last trick, that I would use to bind this all together with listeners, but that you could also use for buttons and arrays : put series into a data structure (array, list, maps, etc.)

     Map<Button, Integer> mapButtonToMediaPlayerResourceId = new HashMap<Button, Integer>(); //then init the map mapButtonToMediaPlayerResourceId.put( button1, R.raw.afternoondelight); //and so on 

Then you can use the same listener for all buttons :

private class OnClickListenerPlaySoundForbutton {
    @Override
    public void onClick(View v) {
        createPlayerForResource( mapButtonToMediaPlayerResourceId.get( (Button) v ) ).start();
    }
}

BTW, for 11 buttons, did you consider using a ListView ?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM